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 [![Ballerina Distribution Build](https://github.com/ballerina-platform/ballerina-distribution/workflows/Build/badge.svg)](https://github.com/ballerina-platform/ballerina-distribution/actions?query=workflow%3A%22Build%22) [![Daily build](https://github.com/ballerina-platform/ballerina-distribution/workflows/Daily%20build/badge.svg)](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, BBE folder structure - `.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). A service example >**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, A service 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, ![Code comment in BAL file](/images/bal-file-comment.png) will get highlighted in the Ballerina website as follows. ![Comment in the BBE](/images/bbe-comment.png) 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. Max character count 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, ![Code comment in BAL file](/images/bal-file-comment.png) it will be displayed in the Ballerina website as follows. ![Comment in the BBE](/images/bbe-comment.png) 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. ![Length of comments](/images/comments-length.png) 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 fileStream = check io:fileReadBlocksAsStream("./local/logFile.txt", 1024); check fileClient->put("/server/logFile.txt", fileStream); check fileStream.close(); } ================================================ FILE: examples/ftp-client-send-file/ftp_client_send_file.md ================================================ # FTP client - Send 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, `put` method is used to write files as byte streams to the FTP server. Use this to transfer files from a local file system to a remote file system. ::: code ftp_client_send_file.bal ::: ## Prerequisites - Start a [FTP server](https://hub.docker.com/r/stilliard/pure-ftpd/) instance. Run the program by executing the following command. The newly-added file will appear in the FTP server. ::: out ftp_client_send_file.out ::: ## Related links - [`ftp:Client->put` method - API documentation](https://lib.ballerina.io/ballerina/ftp/latest#Client#put) - [FTP client - Specification](/spec/ftp/#321-insecure-client) ================================================ FILE: examples/ftp-client-send-file/ftp_client_send_file.metatags ================================================ description: This example demonstrates adding file content on a remote FTP server. keywords: ballerina, ballerina by example, bbe, FTP ================================================ FILE: examples/ftp-client-send-file/ftp_client_send_file.out ================================================ $ bal run ftp_client_write.bal ================================================ FILE: examples/ftp-service-receive-file/ftp_service_receive_file.bal ================================================ import ballerina/ftp; import ballerina/io; // Creates the listener with the connection parameters and the protocol-related // configuration. The listener listens to the files // with the given file name pattern located in the specified path. listener ftp:Listener fileListener = new ({ host: "ftp.example.com", auth: { credentials: { username: "user1", password: "pass456" } }, path: "/home/in", fileNamePattern: "(.*).txt" }); // One or many services can listen to the FTP listener for the periodically-polled // file related events. service on fileListener { // When a file event is successfully received, the `onFileChange` method is called. remote function onFileChange(ftp:WatchEvent & readonly event, ftp:Caller caller) returns error? { // `addedFiles` contains the paths of the newly-added files/directories // after the last polling was called. foreach ftp:FileInfo addedFile in event.addedFiles { // Get the newly added file from the FTP server as a `byte[]` stream. stream fileStream = check caller->get(addedFile.pathDecoded); // Write the content to a file. check io:fileWriteBlocksFromStream(string `./local/${addedFile.name}`, fileStream); check fileStream.close(); } } } ================================================ FILE: examples/ftp-service-receive-file/ftp_service_receive_file.md ================================================ # FTP service - Receive file The `ftp:Service` connects to a given FTP server via the `ftp:Listener`. A `ftp:Listener` is created by providing the host-name and required credentials. Once connected, the service starts receiving events every time a file is deleted or added to the server. To take action for these events `ftp:Caller` is used. The `ftp:Caller` can be specified as a parameter of `onFileChange` remote method. The `ftp:Caller` allows interacting with the server via `get`, `append`, `delete`, etc remote methods. Use this to listen to file changes occurring in a remote file system and take action for those changes. ::: code ftp_service_receive_file.bal ::: ## Prerequisites - Start a [FTP server](https://hub.docker.com/r/stilliard/pure-ftpd/) instance. Run the program by executing the following command. Each newly added file in the SFTP server will be saved in the local file system. ::: out ftp_service_receive_file.out ::: >**Tip:** 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. ## Related links - [`ftp:Listener` client object - API documentation](https://lib.ballerina.io/ballerina/ftp/latest#Listener) - [FTP service - Specification](/spec/ftp/#422-secure-listener) ================================================ FILE: examples/ftp-service-receive-file/ftp_service_receive_file.metatags ================================================ description: This example demonstrates receiving file/directory changes that occur in a remote FTP server and using an ftp:Caller to read the contents of the newly added files. keywords: ballerina, ballerina by example, bbe, FTP, remote file, listener ================================================ FILE: examples/ftp-service-receive-file/ftp_service_receive_file.out ================================================ $ bal run ftp_service_read.bal ================================================ FILE: examples/ftp-service-send-file/ftp_service_send_file.bal ================================================ import ballerina/ftp; import ballerina/io; // Creates the listener with the connection parameters and the protocol-related // configuration. The listener listens to the files // with the given file name pattern located in the specified path. listener ftp:Listener fileListener = check new ({ host: "ftp.example.com", auth: { credentials: { username: "user1", password: "pass456" } }, path: "/home/in", fileNamePattern: "(.*).txt" }); // One or many services can listen to the FTP listener for the periodically-polled // file related events. service on fileListener { // When a file event is successfully received, the `onFileChange` method is called. remote function onFileChange(ftp:WatchEvent & readonly event, ftp:Caller caller) returns error? { foreach ftp:FileInfo addedFile in event.addedFiles { // The `ftp:Caller` can be used to append another file to the added files in the server. stream fileStream = check io:fileReadBlocksAsStream("./local/appendFile.txt", 7); check caller->append(addedFile.pathDecoded, fileStream); check fileStream.close(); } } } ================================================ FILE: examples/ftp-service-send-file/ftp_service_send_file.md ================================================ # FTP service - Send file The `ftp:Service` connects to a given FTP server via the `ftp:Listener`. Once connected, the service starts receiving events every time a file is deleted or added to the server. To take action for these events `ftp:Caller` is used. The `ftp:Caller` can be specified as a parameter of `onFileChange` remote method. The `ftp:Caller` allows interacting with the server via `get`, `append`, `delete`, etc remote methods. Use this to listen to file changes occurring in a remote file system and take action for those changes. ::: code ftp_service_send_file.bal ::: ## Prerequisites - Start a [FTP server](https://hub.docker.com/r/stilliard/pure-ftpd/) instance. Run the program by executing the following command. Each newly added file in the FTP server will be appended with the content in the appending file. ::: out ftp_service_send_file.out ::: >**Tip:** 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. ## Related links - [`ftp:Caller` client object - API documentation](https://lib.ballerina.io/ballerina/ftp/latest#Caller) - [`ftp:Caller` functions - Specification](/spec/ftp/#52-functions) ================================================ FILE: examples/ftp-service-send-file/ftp_service_send_file.metatags ================================================ description: This example demonstrates receiving file/directory changes that occur in a remote FTP server and using an ftp:Caller to append a file to the newly added files. keywords: ballerina, ballerina by example, bbe, FTP, remote file, listener ================================================ FILE: examples/ftp-service-send-file/ftp_service_send_file.out ================================================ $ bal run ftp_service_read_write.bal ================================================ FILE: examples/function-closure/function_closure.bal ================================================ import ballerina/io; public function main() { string[] names = ["Ana", "Alice", "Bob"]; // Define a function to modify and return the variable 'names' that are declared outside the scope. var addName = function(string value) returns string[] { // Access the variable `names` as closure within the `addName` inner function. names.push(value); return names; }; io:println(addName("James")); io:println(names); } ================================================ FILE: examples/function-closure/function_closure.md ================================================ # Function closure The object constructor and function can work as a closure, that can access variables outside of its own scope. It is a compile-time error to have parameter names similar to the outer-scope variable names. ::: code function_closure.bal ::: ::: out function_closure.out ::: ## Related links - [Function values](/learn/by-example/function-values/) - [Anonymous function](/learn/by-example/anonymous-function/) ================================================ FILE: examples/function-closure/function_closure.metatags ================================================ description: This BBE demonstrates handling closures in a function by creating an anonymous function in Ballerina. keywords: anonymous function, ballerina, ballerina by example, bbe, closures, functions, function value ================================================ FILE: examples/function-closure/function_closure.out ================================================ $ bal run function_closure.bal ["Ana","Alice","Bob","James"] ["Ana","Alice","Bob","James"] ================================================ FILE: examples/function-pointers/function_pointers.bal ================================================ import ballerina/io; function add(int v1, int v2) returns int { return v1 + v2; } int num1 = 10; int num2 = 100; // In this example, the function pointer with default values for function pointer parameters is used // as a parameter. function executeWithDefaultValues(function (int a = num1, int b = num2) returns int func) returns int { return func(); } // In this example, the function pointer without default values for the function pointer parameters is used // as a parameter. function execute(function (int, int) returns int func, int v1, int v2) returns int { return func(v1, v2); } public function main() { // The `add` function names serve as a function pointer argument in the // call to the `executeWithDefaultValues` and `execute` functions. io:println("Add num1 & num2: ", executeWithDefaultValues(add)); io:println("Add 1 & 2: ", execute(add, 1, 2)); } ================================================ FILE: examples/function-pointers/function_pointers.md ================================================ # Function pointers Ballerina allows you to define variables (function pointers) of function types. The name of the function variable serves as a reference to that function when it is used in an expression context. A function pointer can be invoked similarly to how a normal function is invoked. ::: code function_pointers.bal ::: ::: out function_pointers.out ::: ## Related links - [Functions values](/learn/by-example/function-values/) - [Default values for function parameters](/learn/by-example/default-values-for-function-parameters/) - [Function types](/learn/by-example/function-types/) ================================================ FILE: examples/function-pointers/function_pointers.metatags ================================================ description: This BBE demonstrates using function pointers in function parameters, passing the default value to the parameters of the function pointers, and creating a function in Ballerina. keywords: ballerina, ballerina by example, bbe, function definition, function pointers, defaultable parameters ================================================ FILE: examples/function-pointers/function_pointers.out ================================================ $ bal run function_pointers.bal Add num1 & num2: 110 Add 1 & 2: 3 ================================================ FILE: examples/function-types/function_types.bal ================================================ import ballerina/io; // Function type syntax. type IntFilter function (int num) returns boolean; // Module-level function definition. function isEven(int n) returns boolean { return n % 2 == 0; } public function main() { // Type of the `evenFunc1` variable is the `IntFilter` function type. IntFilter evenFunc1 = isEven; io:println(evenFunc1(5)); io:println(evenFunc1(6)); // Type of the `evenFunc2` variable is the `function (int num = 5) returns boolean` function type. function (int num = 5) returns boolean evenFunc2 = isEven; // Invoke the function with the default value defined in the function type. io:println(evenFunc2()); // Invoke the function with the passed argument. io:println(evenFunc2(6)); function (int num = 6) returns boolean evenFunc3 = isolated function(int n = 5) returns boolean { return n % 2 == 0; }; // Invoke the function with `6` as the default value for the parameter `num`. io:println(evenFunc3()); } ================================================ FILE: examples/function-types/function_types.md ================================================ # Function types In Ballerina, the function type is a separate basic type. The syntax for a function type looks like a function definition without a function name. When assigning a function value to a variable of the function type, the function signatures must be equal. However, parameters may have default values in either the function value or the function type or both. If a default value is provided in both the function value and function type, the default value in the function type will be used when the function is invoked. ::: code function_types.bal ::: ::: out function_types.out ::: ## Related links - [Function values](/learn/by-example/function-values/) - [Anonymous function](/learn/by-example/anonymous-function/) - [Default values for function parameters](/learn/by-example/default-values-for-function-parameters/) - [Function pointers](/learn/by-example/function-pointers/) ================================================ FILE: examples/function-types/function_types.metatags ================================================ description: This BBE demonstrates defining function types, creating the function definition, and assigning the function value to the function variable in Ballerina. keywords: ballerina, ballerina by example, bbe, functions, function types, function defintion, default parameters ================================================ FILE: examples/function-types/function_types.out ================================================ $ bal run function_types.bal false true false true true ================================================ FILE: examples/function-values/function_values.bal ================================================ import ballerina/io; // Module-level function definition. function isEven(int n) returns boolean { return n % 2 == 0; } public function main() { // The `isEven` function is referred as a value. function (int n) returns boolean f = isEven; // The function values can be executed like regular function calls. io:println(f(5)); io:println(f(6)); } ================================================ FILE: examples/function-values/function_values.md ================================================ # Function values In Ballerina, a function is also a value implying that it can be stored in variables and passed to or returned from the functions. A function value can be executed by calling it. A function value cannot be assigned to a defined function name. ::: code function_values.bal ::: ::: out function_values.out ::: ## Related links - [Functions](/learn/by-example/functions/) - [Function pointers](/learn/by-example/function-pointers/) - [Function types](/learn/by-example/function-types/) ================================================ FILE: examples/function-values/function_values.metatags ================================================ description: This BBE demonstrates creating a variable with the function type and creating a function.  keywords: ballerina, ballerina by example, bbe, functions, function type, function defintion, function value ================================================ FILE: examples/function-values/function_values.out ================================================ $ bal run function_values.bal false true ================================================ FILE: examples/functions/functions.bal ================================================ import ballerina/io; // This function definition has two parameters of type `int`. // The `returns` clause specifies the type of the return value. function add(int x, int y) returns int { int sum = x + y; // The `return` statement returns a value. return sum; } // The function parameters can have default values. function calculateWeight(decimal mass, decimal gForce = 9.8) returns decimal { return mass * gForce; } // The function returns `nil`. function print(anydata data) { io:println(data); } public function main() { // Invoke the function `add` by passing the arguments. int sum = add(5, 11); // A function with no return type does not need a variable assignment. print(sum); // Invoke the `calculateWeight` function with the default arguments. print(calculateWeight(5)); // Invoke the `add` function with the named arguments. print(add(x = 5, y = 6)); // The return value of the function can be ignored by assigning it to `_`. _ = calculateWeight(mass = 5, gForce = 10); } ================================================ FILE: examples/functions/functions.md ================================================ # Functions Functions are declared using the `function` keyword. It accepts zero or more arguments and returns a single value. The `returns` keyword is used to indicate the return type of the function. Function parameters are final variables and cannot be modified within the function. ::: code functions.bal ::: ::: out functions.out ::: ## Related links - [Included record parameters](/learn/by-example/included-record-parameters/) - [Rest Parameters](/learn/by-example/rest-parameters/) - [Default values for function parameters](/learn/by-example/default-values-for-function-parameters/) - [Provide function arguments by name](/learn/by-example/provide-function-arguments-by-name/) - [Rest arguments](/learn/by-example/rest-arguments/) - [Function pointers](/learn/by-example/function-pointers/) - [Function values](/learn/by-example/function-values/) - [Function types](/learn/by-example/function-types/) - [Anonymous function](/learn/by-example/anonymous-function/) - [Function closure](/learn/by-example/function-closure/) ================================================ FILE: examples/functions/functions.metatags ================================================ description: This BBE demonstrates creating functions with defaultable parameters and passing named arguments to the function in Ballerina. keywords: ballerina, ballerina by example, bbe, functions, function defintion, parameters, default parameters, returns ================================================ FILE: examples/functions/functions.out ================================================ $ bal run functions.bal 16 49.0 11 ================================================ FILE: examples/gauge-metrics/gauge_metrics.bal ================================================ import ballerina/http; import ballerina/io; import ballerina/log; import ballerina/observe; import ballerinax/prometheus as _; //Create a gauge as a global variable in the service with the optional field description, //default statistics configurations = { timeWindow: 600000, buckets: 5, // and percentiles: [0.33, 0.5, 0.66, 0.99] }. observe:Gauge globalGauge = new ("global_gauge", "Global gauge defined"); service /onlineStoreService on new http:Listener(9090) { resource function get makeOrder(http:Caller caller, http:Request req) { io:println("------------------------------------------"); //Incrementing the global gauge defined by 15.0. globalGauge.increment(15.0); //Log the current state of global gauge. printGauge(globalGauge); //Create a gauge with simply a name, and default statistics configurations. observe:Gauge localGauge = new ("local_operations"); //Increment the local gauge by default value 1.0. localGauge.increment(); //Increment the value of the gauge by 20. localGauge.increment(20.0); //Decrement the local gauge by default value 1.0. localGauge.decrement(); //Decrement the value of the gauge by 20. localGauge.decrement(10.0); //Log the current state of local gauge. printGauge(localGauge); //Create a gauge with optional fields description, and tags defined. observe:Gauge registeredGaugeWithTags = new ("registered_gauge_with_tags", "RegisteredGauge", {property: "gaugeProperty", gaugeType: "RegisterType"}); //Register the gauge 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. And 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 = registeredGaugeWithTags.register(); if (result is error) { log:printError("Error in registering gauge", 'error = result); } //Set the value of the gauge with the new value. registeredGaugeWithTags.increment(); float value = registeredGaugeWithTags.getValue(); float newValue = value * 12.0; registeredGaugeWithTags.setValue(newValue); //Log the current state of registered gauge with tags. printGauge(registeredGaugeWithTags); //Create a gauge with statistics disabled by passing empty statistics config array. observe:StatisticConfig[] statsConfigs = []; observe:Gauge gaugeWithNoStats = new ("gauge_with_no_stats", "Some description", (), statsConfigs); gaugeWithNoStats.setValue(100); printGauge(gaugeWithNoStats); //Create gauge with custom statistics config. observe:StatisticConfig config = { timeWindow: 30000, percentiles: [0.33, 0.5, 0.9, 0.99], buckets: 3 }; statsConfigs[0] = config; observe:Gauge gaugeWithCustomStats = new ("gauge_with_custom_stats", "Some description", (), statsConfigs); int i = 1; while (i < 6) { gaugeWithCustomStats.setValue((100 * i)); i = i + 1; } //Log the current state of registered gauge with tags. printGauge(gaugeWithCustomStats); io:println("------------------------------------------"); //Send response 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); } } } function printGauge(observe:Gauge gauge) { //Get the statistics snapshot of the gauge. io:print("Gauge - " + gauge.name + " Snapshot: "); observe:Snapshot[]? snapshots = gauge.getSnapshot(); json|error snapshotAsAJson = snapshots.cloneWithType(json); if snapshotAsAJson is json { io:println(snapshotAsAJson.toJsonString()); } //Get the current value of the gauge. io:println("Gauge - ", gauge.name, " Current Value: " , gauge.getValue()); } ================================================ FILE: examples/gauge-metrics/gauge_metrics.client.out ================================================ $ curl http://localhost:9090/onlineStoreService/makeOrder Order Processed! ================================================ FILE: examples/gauge-metrics/gauge_metrics.md ================================================ # Gauge-based metrics Ballerina supports observability out of the box and metrics is one of the three key components of observability. To observe Ballerina code, the build time flag `--observability-included` should be given along with the `Config.toml` file when starting the service. The `Config.toml` file should contain the required runtime configurations related to observability. The developers can define and use metrics to measure their own logic. A gauge is one type of the metrics that is supported by default in Ballerina, and it represents a single numerical value that can arbitrarily go up and down, and also based on the statistics configurations provided to the Gauge, it can also report the statistics such as max, min, mean, percentiles, etc. For more information about configs and observing applications, see [Observe Ballerina programs](/learn/observe-ballerina-programs/). ::: code gauge_metrics.bal ::: Invoke the service using the cURL command below. ::: out gauge_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 gauge_metrics.server.out ::: ================================================ FILE: examples/gauge-metrics/gauge_metrics.metatags ================================================ description: BBE on how to use the default Gauge Metrics Observability feature in Ballerina. keywords: ballerina, ballerina by example, bbe, observability, tracing, opentracing, gauge ================================================ FILE: examples/gauge-metrics/gauge_metrics.server.out ================================================ $ BAL_CONFIG_FILES=Config.toml bal run --observability-included gauge_metrics.bal ballerina: started Prometheus HTTP listener 0.0.0.0:9797 ------------------------------------------ Gauge - global_gauge Snapshot: [{"timeWindow":600000, "mean":15.0, "max":15.0, "min":15.0, "stdDev":0.0, "percentileValues":[{"percentile":0.33, "value":15.0}, {"percentile":0.5, "value":15.0}, {"percentile":0.66, "value":15.0}, {"percentile":0.75, "value":15.0}, {"percentile":0.95, "value":15.0}, {"percentile":0.99, "value":15.0}, {"percentile":0.999, "value":15.0}]}] Gauge - global_gauge Current Value: 15.0 Gauge - local_operations Snapshot: [{"timeWindow":600000, "mean":13.0390625, "max":21.1171875, "min":1.0, "stdDev":8.180620171277893, "percentileValues":[{"percentile":0.33, "value":10.0546875}, {"percentile":0.5, "value":10.0546875}, {"percentile":0.66, "value":20.1171875}, {"percentile":0.75, "value":20.1171875}, {"percentile":0.95, "value":21.1171875}, {"percentile":0.99, "value":21.1171875}, {"percentile":0.999, "value":21.1171875}]}] Gauge - local_operations Current Value: 10.0 Gauge - registered_gauge_with_tags Snapshot: [{"timeWindow":600000, "mean":6.515625, "max":12.0546875, "min":1.0, "stdDev":5.515625, "percentileValues":[{"percentile":0.33, "value":1.0}, {"percentile":0.5, "value":1.0}, {"percentile":0.66, "value":12.0546875}, {"percentile":0.75, "value":12.0546875}, {"percentile":0.95, "value":12.0546875}, {"percentile":0.99, "value":12.0546875}, {"percentile":0.999, "value":12.0546875}]}] Gauge - registered_gauge_with_tags Current Value: 12.0 Gauge - gauge_with_no_stats Snapshot: null Gauge - gauge_with_no_stats Current Value: 100.0 Gauge - gauge_with_custom_stats Snapshot: [{"timeWindow":30000, "mean":300.7, "max":501.5, "min":100.0, "stdDev":141.775033062948, "percentileValues":[{"percentile":0.33, "value":200.5}, {"percentile":0.5, "value":301.5}, {"percentile":0.9, "value":501.5}, {"percentile":0.99, "value":501.5}]}] Gauge - gauge_with_custom_stats Current Value: 500.0 ------------------------------------------ ------------------------------------------ Gauge - global_gauge Snapshot: [{"timeWindow":600000, "mean":22.53125, "max":30.0625, "min":15.0, "stdDev":7.53125, "percentileValues":[{"percentile":0.33, "value":15.0}, {"percentile":0.5, "value":15.0}, {"percentile":0.66, "value":30.0625}, {"percentile":0.75, "value":30.0625}, {"percentile":0.95, "value":30.0625}, {"percentile":0.99, "value":30.0625}, {"percentile":0.999, "value":30.0625}]}] Gauge - global_gauge Current Value: 30.0 Gauge - local_operations Snapshot: [{"timeWindow":600000, "mean":13.0390625, "max":21.1171875, "min":1.0, "stdDev":8.180620171277893, "percentileValues":[{"percentile":0.33, "value":10.0546875}, {"percentile":0.5, "value":10.0546875}, {"percentile":0.66, "value":20.1171875}, {"percentile":0.75, "value":20.1171875}, {"percentile":0.95, "value":21.1171875}, {"percentile":0.99, "value":21.1171875}, {"percentile":0.999, "value":21.1171875}]}] Gauge - local_operations Current Value: 10.0 Gauge - registered_gauge_with_tags Snapshot: [{"timeWindow":600000, "mean":55.4140625, "max":156.9921875, "min":1.0, "stdDev":61.38432884406833, "percentileValues":[{"percentile":0.33, "value":12.0546875}, {"percentile":0.5, "value":12.0546875}, {"percentile":0.66, "value":52.2421875}, {"percentile":0.75, "value":52.2421875}, {"percentile":0.95, "value":156.9921875}, {"percentile":0.99, "value":156.9921875}, {"percentile":0.999, "value":156.9921875}]}] Gauge - registered_gauge_with_tags Current Value: 156.0 Gauge - gauge_with_no_stats Snapshot: null Gauge - gauge_with_no_stats Current Value: 100.0 Gauge - gauge_with_custom_stats Snapshot: [{"timeWindow":30000, "mean":300.7, "max":501.5, "min":100.0, "stdDev":141.775033062948, "percentileValues":[{"percentile":0.33, "value":200.5}, {"percentile":0.5, "value":301.5}, {"percentile":0.9, "value":501.5}, {"percentile":0.99, "value":501.5}]}] Gauge - gauge_with_custom_stats Current Value: 500.0 ------------------------------------------ ------------------------------------------ Gauge - global_gauge Snapshot: [{"timeWindow":600000, "mean":30.0625, "max":45.1875, "min":15.0, "stdDev":12.298479750223873, "percentileValues":[{"percentile":0.33, "value":15.0}, {"percentile":0.5, "value":30.0625}, {"percentile":0.66, "value":30.0625}, {"percentile":0.75, "value":45.1875}, {"percentile":0.95, "value":45.1875}, {"percentile":0.99, "value":45.1875}, {"percentile":0.999, "value":45.1875}]}] Gauge - global_gauge Current Value: 45.0 Gauge - local_operations Snapshot: [{"timeWindow":600000, "mean":13.0390625, "max":21.1171875, "min":1.0, "stdDev":8.180620171277893, "percentileValues":[{"percentile":0.33, "value":10.0546875}, {"percentile":0.5, "value":10.0546875}, {"percentile":0.66, "value":20.1171875}, {"percentile":0.75, "value":20.1171875}, {"percentile":0.95, "value":21.1171875}, {"percentile":0.99, "value":21.1171875}, {"percentile":0.999, "value":21.1171875}]}] Gauge - local_operations Current Value: 10.0 Gauge - registered_gauge_with_tags Snapshot: [{"timeWindow":600000, "mean":377.1927083333333, "max":1887.9921875, "min":1.0, "stdDev":676.7534301455432, "percentileValues":[{"percentile":0.33, "value":12.0546875}, {"percentile":0.5, "value":52.2421875}, {"percentile":0.66, "value":156.9921875}, {"percentile":0.75, "value":157.9921875}, {"percentile":0.95, "value":1887.9921875}, {"percentile":0.99, "value":1887.9921875}, {"percentile":0.999, "value":1887.9921875}]}] Gauge - registered_gauge_with_tags Current Value: 1884.0 Gauge - gauge_with_no_stats Snapshot: null Gauge - gauge_with_no_stats Current Value: 100.0 Gauge - gauge_with_custom_stats Snapshot: [{"timeWindow":30000, "mean":300.7, "max":501.5, "min":100.0, "stdDev":141.775033062948, "percentileValues":[{"percentile":0.33, "value":200.5}, {"percentile":0.5, "value":301.5}, {"percentile":0.9, "value":501.5}, {"percentile":0.99, "value":501.5}]}] Gauge - gauge_with_custom_stats Current Value: 500.0 ------------------------------------------ ================================================ FILE: examples/gauge-metrics/tests/gauge_metrics_test.bal ================================================ import ballerina/test; import ballerina/io; import ballerina/http; @test:Config { } function testFunc() { // Invoking the main function http:Client httpEndpoint = new ("http://localhost:9090"); string response1 = "Order Processed!"; // Send a GET request to the specified endpoint. http:Response|error response = httpEndpoint->get("/onlineStoreService/makeOrder"); if response is http:Response { var res = response.getTextPayload(); if res is error { test:assertFail(msg = "Failed to call the endpoint:"); } else { test:assertEquals(res, response1); } } else { test:assertFail(msg = "Failed to call the endpoint:"); } } function stopService() { test:stopServices("gauge-metrics"); } ================================================ FILE: examples/graphql-client-error-handling/graphql_client_error_handling.bal ================================================ import ballerina/graphql; import ballerina/io; type ProfileResponse record {| *graphql:GenericResponseWithErrors; record {|Profile profile;|} data; |}; type Profile record {| string name; int age; |}; public function main() returns error? { do { graphql:Client graphqlClient = check new ("localhost:9090/graphql"); // This is a malformed GraphQL document. string document = "mutation { updateName(name: 1) { name, age } }"; ProfileResponse response = check graphqlClient->execute(document); io:println(response); } on fail graphql:ClientError err { handleErrors(err); } } function handleErrors(graphql:ClientError clientError) { if clientError is graphql:PayloadBindingError { // This error represents a client-side data binding error. This error occurs due to the // assigned variable type and the value obtained from the wire having a mismatching // shape. io:println("PayloadBindingError: ", clientError.message()); } else if clientError is graphql:InvalidDocumentError { // This error represents GraphQL errors due to GraphQL server-side document // validation. The GraphQL errors returned from the server-side can be obtained by // calling the `detail` method on the `graphql:InvalidDocumentError`. graphql:ErrorDetail[]? errorDetails = clientError.detail().errors; io:println("InvalidDocumentError: ", errorDetails); } else if clientError is graphql:HttpError { // This error represents network-level errors. If the response from the server contains // a body then, it can be obtained by calling the `detail` method on the `graphql:HttpError`. anydata body = clientError.detail().body; io:println("HttpError: ", body, clientError.message()); } } ================================================ FILE: examples/graphql-client-error-handling/graphql_client_error_handling.md ================================================ # GraphQL client - Handle error response The `graphql:Client` allows handling different errors occurred when executing the `execute` method. It returns a `graphql:ClientError` error, which has different subtypes that can be handled differently based on the use case. Use the subtypes of the `graphql:ClientError` to handle different types of errors based on the use case. ::: code graphql_client_error_handling.bal ::: ## Prerequisites - Run the GraphQL service given in the [Mutations](https://ballerina.io/learn/by-example/graphql-mutations/) example. Run the client program by executing the following command. ::: out graphql_client_error_handling.out ::: ## Related links - [`graphql:ClientError` error - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#ClientError) - [GraphQL client error handling - Specification](/spec/graphql/#63-client-error-handling) ================================================ FILE: examples/graphql-client-error-handling/graphql_client_error_handling.metatags ================================================ description: This example demonstrates handling the errors of a GraphQL client. keywords: ballerina, ballerina by example, bbe, graphql, client, error handling, graphql client errors ================================================ FILE: examples/graphql-client-error-handling/graphql_client_error_handling.out ================================================ $ bal run graphql_client_error_handling.bal InvalidDocumentError: [{"message":"String cannot represent non String value: 1","locations":[{"line":1,"column":29}]},{"message":"Field "updateName" argument "id" of type "Int!" is required, but it was not provided.","locations":[{"line":1,"column":12}]}] ================================================ FILE: examples/graphql-client-handle-partial-response/graphql_client_handle_partial_response.bal ================================================ import ballerina/graphql; import ballerina/io; // The `ProfileResponse` is a sub-type of `graphql:GenericResponseWithErrors`. // The `graphql:GenericResponseWithErrors` record represents the generic shape of the GraphQL // response. The `graphql:GenericResponseWithErrors` record contains `data`, `errors`, // and `extensions` fields of which `data` represents the requested data from the GraphQL server, // `errors` represents the field errors raised during the execution, and `extensions` represents // the meta information on the protocol extensions from the GraphQL server. type ProfileResponse record {| *graphql:GenericResponseWithErrors; record {|Profile profile;|} data; |}; // The following record type defines the shape of the response from a GraphQL service, which allows // the `age` field to have a `null` value. type Profile record {| string name; int? age; |}; public function main() returns error? { // Creates a new client with the backend URL. graphql:Client graphqlClient = check new ("localhost:9090/graphql"); string document = "{ profile(id: 1) { name, age } }"; ProfileResponse response = check graphqlClient->execute(document); // Access the data from the response. io:println(response.data); if response.errors !is () { // Access the field errors from the response. io:println(response.errors); } } ================================================ FILE: examples/graphql-client-handle-partial-response/graphql_client_handle_partial_response.md ================================================ # GraphQL client - Handle partial response The `graphql:Client` allows handling cases where a GraphQL service responds with partial data along with errors. To retrieve the partial data, define the fields as nilable types in the expected response type where applicable. Use this approach when the response with partial data is considered to be valid or the partial data needs to be retrieved. >**Hint:** When defining field types as nilable, check the corresponding GraphQL schema to check the nilable fields. ::: code graphql_client_handle_partial_response.bal ::: ## Prerequisites - Run the GraphQL service given in the [Error handling](https://ballerina.io/learn/by-example/graphql-service-error-handling) example. Run the client program by executing the following command. ::: out graphql_client_handle_partial_response.out ::: ## Related links - [`graphql:Client` client object - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#Client) - [`graphql:GenericResponseWithErrors` record - API documentation](https://lib.ballerina.io/ballerina/graphql/1.4.4#GenericResponseWithErrors) - [`graphql:PayloadBindingError` error - API documentation](https://lib.ballerina.io/ballerina/graphql/1.5.0#PayloadBindingError) - [GraphQL client - Specification](/spec/graphql/#25-client) ================================================ FILE: examples/graphql-client-handle-partial-response/graphql_client_handle_partial_response.metatags ================================================ description: This example demonstrates handling partial data returned along with errors, by a GraphQL client. keywords: ballerina, ballerina by example, bbe, graphql, client, query, partial response ================================================ FILE: examples/graphql-client-handle-partial-response/graphql_client_handle_partial_response.out ================================================ $ bal run graphql_client_handle_partial_response.bal {"profile":{"name":"Walter White","age":null}} [{"message":"Error occurred while retrieving age","locations":[{"line":1,"column":26}],"path":["profile","age"]}] ================================================ FILE: examples/graphql-client-query-endpoint/graphql_client_query_endpoint.bal ================================================ import ballerina/graphql; import ballerina/io; // User defined data types to perform client side data-binding. type ProfileResponse record {| record {|Profile profile;|} data; |}; type Profile record {| string name; int age; |}; public function main() returns error? { // Creates a new client with the backend URL. graphql:Client graphqlClient = check new ("localhost:9090/graphql"); string document = "{ profile { name, age } }"; // The `execute` remote method of the `graphql:Client` takes a GraphQL document as the // required argument and sends a request to the specified backend URL seeking a response. On the // retrieval of a successful response, the client tries to perform data binding for the // user-defined data type. On failure to retrieve a successful response or when the client fails // to perform data binding, a `graphql:ClientError` will be returned. ProfileResponse response = check graphqlClient->execute(document); io:println(response.data.profile); } ================================================ FILE: examples/graphql-client-query-endpoint/graphql_client_query_endpoint.md ================================================ # GraphQL client - Query GraphQL endpoint The `graphql:Client` allows connecting and interacting with a GraphQL server. A `graphql:Client` is created by passing the URL of a GraphQL service endpoint. The `execute` method is used to execute a GraphQL operation. This method requires the GraphQL `document` as a required argument and takes a map of `variables` and an `operationName` as arguments optionally, in case the document contains any variables or contains more than one operation. Use the GraphQL client to execute the `Query` and `Mutation` operations on a GraphQL service. ::: code graphql_client_query_endpoint.bal ::: ## Prerequisites - Run the GraphQL service given in the [Record as output object](https://ballerina.io/learn/by-example/graphql-returning-record-values) example. Run the client program by executing the following command. ::: out graphql_client_query_endpoint.out ::: ## Related links - [`graphql:Client` client object - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#Client) - [GraphQL client - Specification](/spec/graphql/#25-client) ================================================ FILE: examples/graphql-client-query-endpoint/graphql_client_query_endpoint.metatags ================================================ description: This example demonstrates connecting and interacting with a GraphQL server via the Ballerina GraphQL client. keywords: ballerina, ballerina by example, bbe, graphql, client, query, mutation ================================================ FILE: examples/graphql-client-query-endpoint/graphql_client_query_endpoint.out ================================================ $ bal run graphql_client_query_endpoint.bal {"name":"Walter White","age":51} ================================================ FILE: examples/graphql-client-security-basic-auth/graphql_client_security_basic_auth.bal ================================================ import ballerina/graphql; import ballerina/io; // User-defined data types to retrieve data from the service. type ProfileResponse record {| *graphql:GenericResponseWithErrors; record {|Profile profile;|} data; |}; type Profile record {| string name; int age; |}; public function main() returns error? { // Defines the GraphQL client to call the APIs secured with basic authentication. graphql:Client graphqlClient = check new ("localhost:9090/graphql", auth = { username: "ldclakmal", password: "ldclakmal@123" }, secureSocket = { cert: "../resource/path/to/public.crt" } ); // Defines the GraphQL document to be sent to the GraphQL service. string document = "{ profile { name, age } }"; // Execute the document and retrieve the response from the GraphQL service. ProfileResponse response = check graphqlClient->execute(document); io:println(response.data.profile); } ================================================ FILE: examples/graphql-client-security-basic-auth/graphql_client_security_basic_auth.md ================================================ # GraphQL client - Basic authentication The `graphql:Client` can connect to a service that is secured with basic authentication by adding the `Authorization: Basic ` header to each request. The username and password for basic authentication can be specified in the `auth` field of the `graphql:ClientConfiguration`. Use this to communicate with the service, which is secured with basic authentication. ::: code graphql_client_security_basic_auth.bal ::: ## Prerequisites - Run the GraphQL service given in the [Basic authentication file user store](/learn/by-example/graphql-service-basic-auth-file-user-store) example. Run the client program by executing the following command. ::: out graphql_client_security_basic_auth.out ::: ## Related links - [`graphql:CredentialsConfig` record - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#CredentialsConfig) - [`auth` module - API documentation](https://lib.ballerina.io/ballerina/auth/latest/) - [GraphQL client basic authentication - Specification](/spec/graphql/#821-basic-authentication) ================================================ FILE: examples/graphql-client-security-basic-auth/graphql_client_security_basic_auth.metatags ================================================ description: This example demonstrates securing a GraphQL client with basic authentication. keywords: ballerina, ballerina by example, bbe, graphql, client, security, basic auth, security ================================================ FILE: examples/graphql-client-security-basic-auth/graphql_client_security_basic_auth.out ================================================ $ bal run graphql_client_security_basic_auth.bal {"name":"Walter White","age":51} ================================================ FILE: examples/graphql-client-security-jwt-authentication/graphql_client_security_jwt_authentication.bal ================================================ import ballerina/graphql; import ballerina/io; // User-defined data types to retrive data from the service. type ProfileResponse record {| *graphql:GenericResponseWithErrors; record {|Profile profile;|} data; |}; type Profile record {| string name; int age; |}; public function main() returns error? { // Defines the GraphQL client to call the JWT Auth secured APIs. graphql:Client graphqlClient = check new ("localhost:9090/graphql", auth = { username: "ballerina", issuer: "wso2", audience: ["ballerina", "ballerina.org", "ballerina.io"], keyId: "5a0b754-895f-4279-8843-b745e11a57e9", jwtId: "JlbmMiOiJBMTI4Q0JDLUhTMjU2In", customClaims: { "scp": "admin" }, expTime: 3600, signatureConfig: { config: { keyFile: "../resource/path/to/private.key" } } }, secureSocket = { cert: "../resource/path/to/public.crt" } ); // Defines the GraphQL document to be sent to the GraphQL service. string document = "{ profile { name, age } }"; // Execute the document and retrieve the response from the GraphQL service. ProfileResponse response = check graphqlClient->execute(document); io:println(response.data.profile); } ================================================ FILE: examples/graphql-client-security-jwt-authentication/graphql_client_security_jwt_authentication.md ================================================ # GraphQL client - JWT authentication The `graphql:Client` can connect to a service that is secured with self-signed JWT by adding the `Authorization: Bearer ` header by passing the `graphql:JwtIssuerConfig` to the `auth` configuration of the client. A self-signed JWT is issued before the request is sent. ::: code graphql_client_security_jwt_authentication.bal ::: ## Prerequisites - Run the GraphQL service given in the [JWT Auth service](/learn/by-example/graphql-service-jwt-auth/) example. Run the client program by executing the command below. ::: out graphql_client_security_jwt_authentication.out ::: ## Related links - [`graphql:JwtIssuerConfig` record - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#JwtIssuerConfig) - [`jwt` module - API documentation](https://lib.ballerina.io/ballerina/jwt/latest/) - [GraphQL client self signed JWT authentication - Specification](/spec/graphql/#823-self-signed-jwt-authentication) ================================================ FILE: examples/graphql-client-security-jwt-authentication/graphql_client_security_jwt_authentication.metatags ================================================ description: This example demonstrates securing a GraphQL client with self-signed JWT Auth. keywords: ballerina, ballerina by example, bbe, graphql, client, security, auth, jwt auth ================================================ FILE: examples/graphql-client-security-jwt-authentication/graphql_client_security_jwt_authentication.out ================================================ $ bal run graphql_client_security_self_signed_jwt_authentication.bal {"name":"Walter White","age":51} ================================================ FILE: examples/graphql-client-security-mutual-ssl/graphql_client_security_mutual_ssl.bal ================================================ import ballerina/graphql; import ballerina/io; // User-defined data types to retrive data from the service. type ProfileResponse record {| *graphql:GenericResponseWithErrors; record {|Profile profile;|} data; |}; type Profile record {| string name; int age; |}; public function main() returns error? { // The GraphQL client can be configured to initiate new connections that are secured via mutual SSL. graphql:Client graphqlClient = check new ("localhost:9090/graphql", secureSocket = { key: { certFile: "../resource/path/to/public.crt", keyFile: "../resource/path/to/private.key" }, cert: "../resource/path/to/public.crt" } ); // Defines the GraphQL document to be sent to the GraphQL service. string document = "{ profile { name, age } }"; // Execute the document and retrieve the response from the GraphQL service. ProfileResponse response = check graphqlClient->execute(document); io:println(response.data.profile); } ================================================ FILE: examples/graphql-client-security-mutual-ssl/graphql_client_security_mutual_ssl.md ================================================ # GraphQL client - Mutual SSL The `graphql:Client` allows opening up a connection secured with mutual SSL (mTLS), which is a certificate-based authentication process in which two parties (the client and server) authenticate each other by verifying the digital certificates. It ensures that both parties are assured of each other's identity. The `graphql:Client` secured with mutual SSL is created by providing the `secureSocket` configurations, which require the client's public certificate as the `certFile`, the client's private key as the `keyFile`, and the server's certificate as the `cert`. Use this to interact with mTLS-encrypted GraphQL servers. ::: code graphql_client_security_mutual_ssl.bal ::: ## Prerequisites - Run the GraphQL service given in the [Mutual SSL](https://ballerina.io/learn/by-example/graphql-service-mutual-ssl) example. Run the client program by executing the following command. ::: out graphql_client_security_mutual_ssl.out ::: ## Related links - [`graphql:ClientSecureSocket` record - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#ClientSecureSocket) - [GraphQL client mutual SSL - Specification](/spec/graphql/#8322-mutual-ssl) ================================================ FILE: examples/graphql-client-security-mutual-ssl/graphql_client_security_mutual_ssl.metatags ================================================ description: This example demonstrates securing a GraphQL client with mutual SSL. keywords: ballerina, ballerina by example, bbe, graphql, client, security, mutual ssl, ssl protocols, ciphers ================================================ FILE: examples/graphql-client-security-mutual-ssl/graphql_client_security_mutual_ssl.out ================================================ $ bal run graphql_client_security_mutual_ssl.bal {"name":"Walter White","age":51} ================================================ FILE: examples/graphql-client-security-oauth2-password-grant-type/graphql_client_security_oauth2_password_grant_type.bal ================================================ import ballerina/graphql; import ballerina/oauth2; import ballerina/io; // User-defined data types to retrive data from the service. type ProfileResponse record {| *graphql:GenericResponseWithErrors; record {|Profile profile;|} data; |}; type Profile record {| string name; int age; |}; public function main() returns error? { // Defines the GraphQL client to call the OAuth2-secured APIs. graphql:Client graphqlClient = check new ("localhost:9090/graphql", auth = { tokenUrl: "https://localhost:9445/oauth2/token", username: "admin", password: "admin", clientId: "FlfJYKBD2c925h4lkycqNZlC2l4a", clientSecret: "PJz0UhTJMrHOo68QQNpvnqAY_3Aa", scopes: "admin", refreshConfig: oauth2:INFER_REFRESH_CONFIG, clientConfig: { secureSocket: { cert: "../resource/path/to/public.crt" } } }, secureSocket = { cert: "../resource/path/to/public.crt" } ); // Defines the GraphQL document to be sent to the GraphQL service. string document = "{ profile { name, age } }"; // Execute the document and retrieve the response from the GraphQL service. ProfileResponse response = check graphqlClient->execute(document); io:println(response.data.profile); } ================================================ FILE: examples/graphql-client-security-oauth2-password-grant-type/graphql_client_security_oauth2_password_grant_type.md ================================================ # GraphQL client - OAuth2 password grant type The `graphQL:Client` can connect to a service that is secured with the OAuth2 password grant type by adding the `Authorization: Bearer ` header to each request. The required configurations for this grant type can be specified in the `auth` field of the `graphql:ClientConfiguration`. Use this grant type when you need to exchange the user's credentials for an access token. ::: code graphql_client_security_oauth2_password_grant_type.bal ::: ## Prerequisites - Run the GraphQL service given in the [OAuth2 service](/learn/by-example/graphql-service-oauth2/) example. Run the client program by executing the command below. ::: out graphql_client_security_oauth2_password_grant_type.out ::: ## Related links - [`graphql:OAuth2PasswordGrantConfig` record - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#OAuth2PasswordGrantConfig) - [`oauth2` module - API documentation](https://lib.ballerina.io/ballerina/oauth2/latest/) - [GraphQL client OAuth2 password grant type - Specification](/spec/graphql/#8242-password-grant-type) ================================================ FILE: examples/graphql-client-security-oauth2-password-grant-type/graphql_client_security_oauth2_password_grant_type.metatags ================================================ description: This example demonstrates securing a GraphQL client with the OAuth2 password grant type. keywords: ballerina, ballerina by example, bbe, graphql, client, security, auth, oauth2, password grant type ================================================ FILE: examples/graphql-client-security-oauth2-password-grant-type/graphql_client_security_oauth2_password_grant_type.out ================================================ $ bal run graphql_client_security_oauth2_password_grant_type.bal {"name":"Walter White","age":51} ================================================ FILE: examples/graphql-client-security-ssl-tls/graphql_client_security_ssl_tls.bal ================================================ import ballerina/graphql; import ballerina/io; // User-defined data types to retrive data from the service. type ProfileResponse record {| *graphql:GenericResponseWithErrors; record {|Profile profile;|} data; |}; type Profile record {| string name; int age; |}; public function main() returns error? { // Defines the GraphQL client with secure socket configurations. graphql:Client graphqlClient = check new ("localhost:9090/graphql", secureSocket = { cert: "../resource/path/to/public.crt" } ); // Defines the GraphQL document to be sent to the GraphQL service. string document = "{ profile { name, age } }"; // Execute the document and retrieve the response from the GraphQL service. ProfileResponse response = check graphqlClient->execute(document); io:println(response.data.profile); } ================================================ FILE: examples/graphql-client-security-ssl-tls/graphql_client_security_ssl_tls.md ================================================ # GraphQL client - SSL/TLS The `graphql:Client` can be configured to communicate through HTTPS by providing a certificate file. The certificate can be provided through the `secureSocket` field of the `graphql:ClientConfiguration`. Use this to secure the communication between the client and the server. ::: code graphql_client_security_ssl_tls.bal ::: ## Prerequisites - Run the GraphQL service given in the [SSL/TLS](https://ballerina.io/learn/by-example/graphql-returning-record-values) example. Run the client program by executing the following command. ::: out graphql_client_security_ssl_tls.out ::: ## Related links - [`graphql:ClientSecureSocket` record - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#ClientSecureSocket) - [GraphQL client SSL/TLS - Specification](/spec/graphql/#8311-ssltls) ================================================ FILE: examples/graphql-client-security-ssl-tls/graphql_client_security_ssl_tls.metatags ================================================ description: This example demonstrates securing a GraphQL client with SSL. keywords: ballerina, ballerina by example, bbe, graphql, client, security, ssl, tls ================================================ FILE: examples/graphql-client-security-ssl-tls/graphql_client_security_ssl_tls.out ================================================ $ bal run graphql_client_security_ssl_tls.bal {"name":"Walter White","age":51} ================================================ FILE: examples/graphql-context/graphql_context.1.client.out ================================================ $ curl -X POST -H "Content-type: application/json" -H "scope: admin" -d '{ "query": "{ profile { name salary } }" }' 'http://localhost:9090/graphql' {"data":{"profile":{"name":"Walter White", "salary":737000.0}}} ================================================ FILE: examples/graphql-context/graphql_context.2.client.out ================================================ $ curl -X POST -H "Content-type: application/json" -H "scope: unknown" -d '{ "query": "{ profile { name salary } }" }' 'http://localhost:9090/graphql' {"errors":[{"message":"Permission denied", "locations":[{"line":1, "column":3}], "path":["profile"]}], "data":null} ================================================ FILE: examples/graphql-context/graphql_context.bal ================================================ import ballerina/graphql; import ballerina/http; @graphql:ServiceConfig { // Initialization of the `graphqlContext` should be provided to the `contextInit` field. contextInit } service /graphql on new graphql:Listener(9090) { // Defines a `Profile` field inside the service. private final Profile profile; function init() { // Initializes the `profile` value. self.profile = new ("Walter White", 51, 737000.00); } // If the context is needed, it should be defined as a parameter of the resolver function. resource function get profile(graphql:Context context) returns Profile|error { // The profile information will be returned only if the scope is `admin` or `user`. check validateScope(context, ["admin", "user"]); return self.profile; } } // Defines a service class to use as an object in the GraphQL service. service class Profile { private final string name; private final int age; private final float salary; function init(string name, int age, float salary) { self.name = name; self.age = age; self.salary = salary; } resource function get name() returns string => self.name; resource function get age() returns int => self.age; // If the context is needed, it should just be specified as a parameter of the resolver method. // Ballerina handles propagating the context, and therefore, it is not required to be passed // as an argument to the `init` method from the parent resolver. resource function get salary(graphql:Context context) returns float|error { // The salary information will be returned only if the scope is `admin`. check validateScope(context, ["admin"]); return self.salary; } } isolated function validateScope(graphql:Context context, string[] allowedScopes) returns error? { // Retrieves the `scope` attribute from the context. This will return a `graphql:Error` if // the `scope` is not found in the context. final string scope = check context.get("scope").ensureType(); // If the scope doesn't matches any of the allowed scopes return an `error`. if !allowedScopes.some(allowedScope => scope == allowedScope) { // Returns an `error` if the required scope is not found. return error("Permission denied"); } } isolated function contextInit(http:RequestContext requestContext, http:Request request) returns graphql:Context|error { // Initialize the `graphql:Context` object. graphql:Context context = new; // Retrieves the header named `scope` from the `http:request` and set it to the context with // the `scope` key. If the header does not exist, this will return an `error`, and thereby, // the request will not be processed. context.set("scope", check request.getHeader("scope")); // Finally, the context object should be returned. return context; } ================================================ FILE: examples/graphql-context/graphql_context.graphql ================================================ { profile { name salary } } ================================================ FILE: examples/graphql-context/graphql_context.md ================================================ # GraphQL service - Context object The Ballerina `graphql` module allows defining and using a `graphql:Context` object. The `contextInit` field in the `graphql:ServiceConfig` annotation can be used to pass the context initialization function. If it is not provided, a default, empty `context` object will be created per request. When the `graphql:Context` is needed to be accessed, define it as a parameter of the `resource`/`remote` method. Use the `graphql:Context` to pass meta information between the `resource`/`remote` methods used as GraphQL object fields. >**Hint:** The `graphql:Context` is defined before the other parameters of a function as a convention. >**Note:** If the `graphql:Context` is defined as a parameter of a resolver function, it will be accessible inside the resolver. Passing it down is not necessary. ::: code graphql_context.bal ::: Run the service by executing the following command. ::: out graphql_context.server.out ::: Send the following document to the GraphQL endpoint to test the service. ::: code graphql_context.graphql ::: To send the document, execute the following cURL command in a separate terminal. First, send the request with the `scope` header value set to `admin`. ::: out graphql_context.1.client.out ::: Now, send the same document with the `scope` header value set to `unknown`. This will return an error in the `profile` field. ::: out graphql_context.2.client.out ::: >**Tip:** You can invoke the above service via the [GraphQL client](/learn/by-example/graphql-client-query-endpoint/). ## Related links - [`graphql:Context` object - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#Context) - [GraphQL context - Specification](/spec/graphql/#101-context-object) ================================================ FILE: examples/graphql-context/graphql_context.metatags ================================================ description: This example demonstrates defining and using a GraphQL context object. keywords: ballerina, ballerina by example, bbe, graphql, context object ================================================ FILE: examples/graphql-context/graphql_context.server.out ================================================ $ bal run graphql_context.bal ================================================ FILE: examples/graphql-dataloader/graphql_dataloader.bal ================================================ import ballerina/graphql; import ballerina/graphql.dataloader; import ballerina/http; import ballerina/log; 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} ]; // Implement the `batch load` function for the data loader. // This function handles fetching data in batches. The primary keys of the data to be loaded // will be provided as the values of the `ids` parameter to the batch load function. The expected output // of this function is an array of results in which each element in the result array corresponds // to a primary key from the `ids` array. isolated function bookLoaderFunction(readonly & anydata[] ids) returns BookRow[][]|error { final int[] keys = check ids.ensureType(); log:printInfo("executing bookLoaderFunction", keys = keys); // Implement the batching logic. 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. isolated function preBooks(graphql:Context ctx) { // Obtain the dataloader from the context using the unique name. // Providing an invalid name here will lead to a panic. dataloader:DataLoader bookLoader = ctx.getDataLoader("bookLoader"); // Add the primary key of the data to be fetched to the dataloader. bookLoader.add(self.author.id); } isolated resource function get books(graphql:Context ctx) returns Book[]|error { // Obtain the dataloader from the context using the unique name. dataloader:DataLoader bookLoader = ctx.getDataLoader("bookLoader"); // Obtain the data from the dataloader using the primary key of the data. 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; // Register the dataloader with the context using a unique name. // A defult implementation of the dataloader is used here. ctx.registerDataLoader("bookLoader", new dataloader:DefaultDataLoader(bookLoaderFunction)); return ctx; } ================================================ FILE: examples/graphql-dataloader/graphql_dataloader.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/graphql-dataloader/graphql_dataloader.graphql ================================================ { authors { name books { title } } } ================================================ FILE: examples/graphql-dataloader/graphql_dataloader.md ================================================ # GraphQL service - Dataloader The Ballerina GraphQL module provides the capability to batch and cache data fetching from data sources using the `graphql.dataloader` submodule. To leverage this functionality in a GraphQL service, you must register data loaders through the `graphql:Context` object and implement the corresponding prefetch method logic and resolver method logic. 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 follows the convention of having the prefix `pre`, followed by the resolver method name. The use of `graphql.dataloader` avoids excessive data fetching, effectively addressing the GraphQL N+1 problem. ::: code graphql_dataloader.bal ::: Run the service by executing the following command. ::: out graphql_dataloader.server.out ::: Send the following document to the GraphQL endpoint to test the service. ::: code graphql_dataloader.graphql ::: To send the document, execute the following cURL command in a separate terminal. ::: out graphql_dataloader.client.out ::: ## Related links - [`garaphql.dataloader` module - API documentation](https://lib.ballerina.io/ballerina/graphql.dataloader/latest) - [DataLoader - Specification](/spec/graphql/#106-dataloader) - [Utilizing multiple dataloaders in a graphql service](/spec/graphql/#example-utilizing-multiple-dataloaders-in-a-graphql-service) ================================================ FILE: examples/graphql-dataloader/graphql_dataloader.metatags ================================================ description: This example demonstrates engaging data loaders with a GraphQL service. keywords: ballerina, ballerina by example, bbe, graphql, dataloader, batching, caching ================================================ FILE: examples/graphql-dataloader/graphql_dataloader.server.out ================================================ $ bal run graphql_dataloader.bal time = 2023-08-08T13:58:40.479+05:30 level = INFO module = "" message = "executing bookLoaderFunction" keys = [1,2] ================================================ FILE: examples/graphql-directives/graphql_directives.1.client.out ================================================ $ curl -X POST -H "Content-type: application/json" -d '{ "query": "{ profile { name, gender @skip(if: true) } }" }' 'http://localhost:9090/graphql' {"data":{"profile":{"name":"Walter White"}}} ================================================ FILE: examples/graphql-directives/graphql_directives.1.graphql ================================================ { profile { name gender @skip(if: true) } } ================================================ FILE: examples/graphql-directives/graphql_directives.2.client.out ================================================ $ curl -X POST -H "Content-type: application/json" -d '{ "query": "{ profile { name, gender @include(if: true) } }" }' 'http://localhost:9090/graphql' {"data":{"profile":{"name":"Walter White", "gender":"MALE"}}} ================================================ FILE: examples/graphql-directives/graphql_directives.2.graphql ================================================ { profile { name gender @include(if: true) { } } ================================================ FILE: examples/graphql-directives/graphql_directives.3.client.out ================================================ $ curl -X POST -H "Content-type: application/json" -d '{ "query": "{ __type(name: \"Gender\") { name, enumValues(includeDeprecated: true) { name, isDeprecated, deprecationReason } } }" }' 'http://localhost:9090/graphql' {"data":{"__type":{"name":"Gender", "enumValues":[{"name":"OTHER", "isDeprecated":false, "deprecationReason":null}, {"name":"NON_BINARY", "isDeprecated":true, "deprecationReason":"The `NON_BINARY` is deprecated. Use `OTHER` instead."}, {"name":"FEMALE", "isDeprecated":false, "deprecationReason":null}, {"name":"MALE", "isDeprecated":false, "deprecationReason":null}]}}} ================================================ FILE: examples/graphql-directives/graphql_directives.3.graphql ================================================ { __type(name: "Gender") { name enumValues { name isDeprecated deprecationReason } } } ================================================ FILE: examples/graphql-directives/graphql_directives.bal ================================================ import ballerina/graphql; type Profile record { string name; int age; Gender gender; }; // Marks enum value as deprecated. enum Gender { MALE, FEMALE, # # Deprecated # The `NON_BINARY` is deprecated. Use `OTHER` instead. @deprecated NON_BINARY, OTHER } service /graphql on new graphql:Listener(9090) { // Marks a field as deprecated. # # Deprecated # The `profileInfo` field is deprecated. Use `profile` instead. @deprecated resource function get profileInfo() returns Profile { return { name: "Walter White", age: 51, gender: MALE }; } resource function get profile() returns Profile { return { name: "Walter White", age: 51, gender: MALE }; } } ================================================ FILE: examples/graphql-directives/graphql_directives.md ================================================ # GraphQL service - Directives The Ballerina `graphql` module allows using the following set of pre-defined directives. - `@deprecated` - The `@deprecated` annotation can be used as the `deprecated` directive on the `resource`/`remote` methods and `enum` values. Use this to mark a field or an enum value as deprecated. - `@skip(if: Boolean!)` - The `@skip` directive can be used on fields or fragments in GraphQL documents. Use this to skip a field execution based on the given condition. - `@include(if: Boolean!)` - The `@include` directive can be used on fields or fragments in GraphQL documents. Use this to include a field execution based on the given condition. ::: code graphql_directives.bal ::: Run the service by executing the following command. ::: out graphql_directives.server.out ::: The requests below demonstrate the usage of the `@skip` and `@include` directives. Invoke the service using the following cURL command to inspect the result. Send the following document containing the `@skip` directive to test it. ::: code graphql_directives.1.graphql ::: To send the document, execute the following cURL command in a separate terminal. ::: out graphql_directives.1.client.out ::: Then, send the following document containing the `@include` directive. ::: code graphql_directives.2.graphql ::: To send the document, execute the following cURL command. ::: out graphql_directives.2.client.out ::: Finally, send the following document with an introspection query to check the deprecated enum values. ::: code graphql_directives.3.graphql ::: To send the document, execute the following cURL command. ::: out graphql_directives.3.client.out ::: >**Tip:** You can invoke the above service via the [GraphQL client](/learn/by-example/graphql-client-query-endpoint/). ## Related links - [`graphql` module - API documentation](https://lib.ballerina.io/ballerina/graphql/latest) - [GraphQL directives - Specification](/spec/graphql/#5-directives) ================================================ FILE: examples/graphql-directives/graphql_directives.metatags ================================================ description: This example demonstrates using GraphQL directives in Ballerina. keywords: ballerina, ballerina by example, bbe, graphql, directives ================================================ FILE: examples/graphql-directives/graphql_directives.server.out ================================================ $ bal run graphql_directives.bal ================================================ FILE: examples/graphql-documentation/graphql_documentation.bal ================================================ import ballerina/graphql; // All the types that are used in the GraphQL service can have documentation. # Represents a profile. # + name - The name of the profile # + age - The age of the profile type Profile record {| string name; int age; |}; service /graphql on new graphql:Listener(9090) { // Add documentation to reflect them in the generated GraphQL schema. # Returns a profile using the provided ID. # + id - The ID of the profile # + return - The profile with the requested ID resource function get profile(int id) returns Profile? { if id == 1 { return {name: "Walter White", age: 52}; } else if id == 2 { return {name: "Jesse Pinkman", age: 25}; } return; } } ================================================ FILE: examples/graphql-documentation/graphql_documentation.client.out ================================================ $ curl -X POST -H "Content-type: application/json" -d '{ "query": "{ __schema { queryType { fields { name description type { name description fields { name description } } args { name description } } } } }" }' 'http://localhost:9090/graphql' { "data": { "__schema": { "queryType": { "fields": [ { "name": "profile", "description": "Returns a profile using the provided id.", "type": { "name": "Profile", "description": "Represents a Profile.", "fields": [ { "name": "name", "description": "The name of the profile" }, { "name": "age", "description": "The age of the profile" } ] }, "args": [ { "name": "id", "description": "The ID of the profile" } ] } ] } } } } ================================================ FILE: examples/graphql-documentation/graphql_documentation.graphql ================================================ { __schema { queryType { fields { name description type { name description fields { name description } } args { name description } } } } } ================================================ FILE: examples/graphql-documentation/graphql_documentation.md ================================================ # GraphQL service - Documentation The Ballerina `graphql` module allows adding documentation to the generated GraphQL schema and its subsequent types. To add documentation, use Ballerina documentation for the `graphql:Service`, `resource`/`remote` methods, types, and `enum`s. The Ballerina documentation will be automatically added as the documentation in the generated GraphQL schema. ::: code graphql_documentation.bal ::: Run the service by executing the following command. ::: out graphql_documentation.server.out ::: Send the following document with an introspection query to test how the documentation is added to the schema. ::: code graphql_documentation.graphql ::: To send the document, execute the following cURL command in a separate terminal. ::: out graphql_documentation.client.out ::: >**Tip:** You can invoke the above service via the [GraphQL client](/learn/by-example/graphql-client-query-endpoint/). ## Related links - [`graphql` module - API documentation](https://lib.ballerina.io/ballerina/graphql/latest) - [GraphQL documentation - Specification](/spec/graphql/#35-documentation) ================================================ FILE: examples/graphql-documentation/graphql_documentation.metatags ================================================ description: This example demonstrates adding documentation to a GraphQL service and its subsequent types. keywords: ballerina, ballerina by example, bbe, graphql, service, documentation ================================================ FILE: examples/graphql-documentation/graphql_documentation.server.out ================================================ $ bal run graphql_documentation.bal ================================================ FILE: examples/graphql-field-interceptors/graphql_field_interceptors.bal ================================================ import ballerina/graphql; import ballerina/log; // Defines an interceptor named `LogInterceptor` using a service class. It cannot have any // `resource`/`remote` methods except the `execute()` remote method. Other methods are allowed. readonly service class LogInterceptor { // Includes the `graphql:Interceptor` service object from the GraphQL package. *graphql:Interceptor; // Implement the `execute()` remote method provided by the `graphql:Interceptor` object. isolated remote function execute(graphql:Context context, graphql:Field 'field) returns anydata|error { // Access the current execution field name using the `graphql:Field` object. string fieldName = 'field.getName(); // This log statement executes before the resolver execution. log:printInfo(string `Field "${fieldName}" execution started!`); // The `context.resolve()` function can be used to invoke the next interceptor. var data = context.resolve('field); // This log statement executes after the resolver execution. log:printInfo(string `Field "${fieldName}" execution completed!`); // Returns the execution result of the next interceptor or the resolver. return data; } } service /graphql on new graphql:Listener(9090) { @graphql:ResourceConfig { // Interceptor instances should be inserted using the `interceptors` field. A single // interceptor or an array of interceptors can be provided. The execution order of the // interceptor will be the order of the interceptors. interceptors: new LogInterceptor() } isolated resource function get name() returns string { log:printInfo("Executing the field \"name\""); return "GraphQL Interceptors"; } } ================================================ FILE: examples/graphql-field-interceptors/graphql_field_interceptors.client.out ================================================ $ curl -X POST -H "Content-type: application/json" -d '{ "query": "{ name }"}' 'http://localhost:9090/graphql' {"data":{"name":"GraphQL Interceptors"}} ================================================ FILE: examples/graphql-field-interceptors/graphql_field_interceptors.graphql ================================================ { name } ================================================ FILE: examples/graphql-field-interceptors/graphql_field_interceptors.md ================================================ # GraphQL service - Field interceptors The GraphQL resolver functions allow adding interceptors for GraphQL requests to execute custom logic. An interceptor can be defined using a `readonly` class that includes the `graphql:Interceptor` type. The interceptor class must implement the `execute` remote method, which is defined in the `graphql:Interceptor` service object type. It can be passed as a single interceptor or an array of interceptors using the `interceptors` field in the `graphql:ResourceConfig` annotation. The provided interceptors will be executed using the _onion principle_. Use the field interceptors to execute custom logic before and after executing a `resource` or a `remote` method that needs to be separated from the business logic. >**Note:** A resolver can have zero or more interceptors. ::: code graphql_field_interceptors.bal ::: Run the service by executing the following command. ::: out graphql_field_interceptors.server.out ::: Send the following document to the GraphQL endpoint to test the service. ::: code graphql_field_interceptors.graphql ::: To send the document, execute the following cURL command in a separate terminal. ::: out graphql_field_interceptors.client.out ::: >**Tip:** You can invoke the above service via the [GraphQL client](/learn/by-example/graphql-client-query-endpoint/). ## Related links - [`graphql:Interceptor` object - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#Interceptor) - [GraphQL field interceptors - Specification](/spec/graphql/#10332-field-interceptors) ================================================ FILE: examples/graphql-field-interceptors/graphql_field_interceptors.metatags ================================================ description: This example demonstrates using interceptors on GraphQL remote and resource methods to execute custom logic. keywords: ballerina, ballerina by example, bbe, graphql, service, interceptors, resolver functions ================================================ FILE: examples/graphql-field-interceptors/graphql_field_interceptors.server.out ================================================ $ bal run graphql_field_interceptors.bal # when executing the query, following statements are logged in the terminal. time = 2023-04-04T10:26:10.795+05:30 level = INFO module = "" message = "Field \"name\" execution started!" time = 2023-04-04T10:26:10.813+05:30 level = INFO module = "" message = "Executing the field \"name\"" time = 2023-04-04T10:26:10.824+05:30 level = INFO module = "" message = "Field \"name\" execution completed!" ================================================ FILE: examples/graphql-file-upload/graphql_file_upload.bal ================================================ import ballerina/graphql; import ballerina/io; service /fileUpload on new graphql:Listener(9090) { // Remote methods can use the `graphql:Upload` type as an input parameter type. remote function fileUpload(graphql:Upload file) returns string|error { // The uploaded file information can be accessed using the `graphql:Upload` type. string fileName = file.fileName; string path = string `./uploads/${fileName}`; // Accesses the byte stream of the file from the `graphql:Upload` type. The type of the // `byteStream` field is `stream`. stream byteStream = file.byteStream; // Stores the received file using the ballerina `io` package. If any `error` occurred during // the file write, it can be returned as the resolver function output. check io:fileWriteBlocksFromStream(path, byteStream); // Returns the message if the uploading process is successful. return string `File ${fileName} successfully uploaded`; } resource function get getUploadedFileNames() returns string[] { return ["image1.png", "image2.png"]; } } ================================================ FILE: examples/graphql-file-upload/graphql_file_upload.client.out ================================================ $ curl localhost:9090/fileUpload -F operations='{ "query": "mutation($file: Upload!) { fileUpload(file: $file) }", "variables": { "file": null } }' -F map='{ "0": ["variables.file"] }' -F 0=@file1.png {"data":{"fileUpload":"File file1.png successfully uploaded"}} ================================================ FILE: examples/graphql-file-upload/graphql_file_upload.md ================================================ # GraphQL service - File upload The Ballerina `graphql` module allows uploading files to a GraphQL schema. To enable file uploading in a GraphQL service, add the `graphql:Upload` record as an input parameter of a `remote` method inside a `graphql:Service`. The `graphql:Upload` record includes the details of the file that is being uploaded. The GraphQL file upload follows the [_Graphql Multipart Request Spec_](https://github.com/jaydenseric/graphql-multipart-request-spec/tree/master) to upload the files. Use this to create a GraphQL API where users can upload files. ::: code graphql_file_upload.bal ::: Run the service by executing the following command. ::: out graphql_file_upload.server.out ::: To upload a file, send an HTTP multipart request to the GraphQL endpoint using the following cURL command. The first part of the request is `operations` field that includes a `JSON-encoded` map. This field is similar to a standard HTTP POST request that being sent to a GraphQL endpoint, except where the variable values related to the file upload should be `null`. The second part of the request is the `map` field, which is a `JSON-encoded` map. It contains a mapping between the variables defined in the first part of the request and the files that are mentioned in the next part. The `key` is used to map a file using the key provided in the next part of the request, and the value is mapped to the variable name defined in the previous part. Multiple variables can have the same file, so the value is an array. Next part contains the unique key for each file and the path for each file. The `key` in this part should be the same key used in the previous part. Following is a complete cURL request to send a multipart request to upload files to the GraphQL service. ::: out graphql_file_upload.client.out ::: This will create a directory `uploads` where the service is running, and then saves the `file1.png` inside it. ## Related links - [`graphql:Upload` record - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#Upload) - [GraphQL multipart request specification](https://github.com/jaydenseric/graphql-multipart-request-spec) - [GraphQL file upload - Specification](/spec/graphql/#104-file-upload) ================================================ FILE: examples/graphql-file-upload/graphql_file_upload.metatags ================================================ description: This example demonstrates implementing a GraphQL service with the file uploading capability. keywords: ballerina, ballerina by example, bbe, graphql, file upload ================================================ FILE: examples/graphql-file-upload/graphql_file_upload.server.out ================================================ $ bal run graphql_file_upload.bal ================================================ FILE: examples/graphql-graphiql/graphql_graphiql.bal ================================================ import ballerina/graphql; // Enables the GraphiQL client with the provided path. @graphql:ServiceConfig { graphiql: { enabled: true, // Path is optional, if not provided, it will be dafulted to `/graphiql`. path: "/testing" } } service /graphql on new graphql:Listener(9090) { resource function get greeting() returns string { return "Hello, World"; } } ================================================ FILE: examples/graphql-graphiql/graphql_graphiql.md ================================================ # GraphQL service - GraphiQL client The Ballerina `graphql` module includes a built-in GraphiQL client. To enable the GraphiQL client, use the `graphiql` field in the `graphql:ServiceConfig` annotation on a `graphql:Service`. The GraphiQL client can be used to test the GraphQL APIs using the GraphiQL IDE. This example shows how to enable the GraphiQL client for a Ballerina GraphQL service. ::: code graphql_graphiql.bal ::: Run the service by executing the following command. ::: out graphql_graphiql.out ::: To access the GraphiQL client, open a browser and access `http://localhost:9090/testing`. Following is a sample screenshot of the GraphiQL client. ![GraphiQL client](/learn/by-example/images/graphiql-client.png "GraphiQL Client") ## Related links - [`graphql:ServiceConfig` annotation - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#ServiceConfig) - [`graphql:GraphiQL` record - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#Graphiql) - [GraphQL GraphiQL client - Specification](/spec/graphql/#91-graphiql-client) - [GraphQL GraphiQL client configuration - Specification](/spec/graphql/#715-graphiql-configurations) ================================================ FILE: examples/graphql-graphiql/graphql_graphiql.metatags ================================================ description: This example demonstrates enabling the built-in GraphiQL client for a GraphQL service. keywords: ballerina, ballerina by example, bbe, graphql, graphiql ================================================ FILE: examples/graphql-graphiql/graphql_graphiql.out ================================================ $ bal run graphql_graphiql.bal ================================================ FILE: examples/graphql-hello-world/graphql_hello_world.bal ================================================ import ballerina/graphql; // Service attached to a GraphQL listener exposes a GraphQL service on the provided port. service /graphql on new graphql:Listener(9090) { // A resource method with `get` accessor inside a `graphql:Service` represents a field in the // root `Query` type. resource function get greeting() returns string { return "Hello, World"; } } ================================================ FILE: examples/graphql-hello-world/graphql_hello_world.client.out ================================================ $ curl -X POST -H "Content-type: application/json" -d '{ "query": "{ greeting }" }' 'http://localhost:9090/graphql' {"data":{"greeting":"Hello, World"}} ================================================ FILE: examples/graphql-hello-world/graphql_hello_world.graphql ================================================ { greeting } ================================================ FILE: examples/graphql-hello-world/graphql_hello_world.md ================================================ # GraphQL service - Hello world A `graphql:Service` in Ballerina represents a GraphQL schema. Each resource method of the `graphql:Service` with the `get` accessor represents a resolver function in the root `Query` type. The return type of the `resource` method will be the type of field represented by that resource method. >**Note:** GraphQL queries are expected to be read-only operations that are usually executed against entities such as `Person`, `Profile`, `Address`, etc. Ballerina uses `resource` methods to handle such cases. Therefore, these `resource` methods are usually named using nouns with `get` accessor. ::: code graphql_hello_world.bal ::: Run the service by executing the following command. ::: out graphql_hello_world.server.out ::: Send the following document to the GraphQL endpoint to test the service. ::: code graphql_hello_world.graphql ::: To send the document, execute the following cURL command in a separate terminal. ::: out graphql_hello_world.client.out ::: >**Tip:** You can invoke the above service via the [GraphQL client](/learn/by-example/graphql-client-query-endpoint/). ## Related links - [`graphql` module - API documentation](https://lib.ballerina.io/ballerina/graphql/latest) - [GraphQL `Query` type - Specification](/spec/graphql/#311-the-query-type) ================================================ FILE: examples/graphql-hello-world/graphql_hello_world.metatags ================================================ description: This example demonstrates a GraphQL service endpoint written in Ballerina. keywords: ballerina, ballerina by example, bbe, graphql, service endpoint ================================================ FILE: examples/graphql-hello-world/graphql_hello_world.server.out ================================================ $ bal run graphql_hello_world.bal ================================================ FILE: examples/graphql-hierarchical-resource-paths/graphql_hierarchical_resource_paths.bal ================================================ import ballerina/graphql; // This service has multiple resources with hierarchical resource paths. Since all the resource // paths starts with `profile`, the root `Query` operation will have a single field named `profile`. // The type of this field is `profile!`. (For hierarchical paths, the field name and the // type name will be the same). The `profile` type has two fields: `quote` and `name`. The type of // the `quote` field is `String!` and the type of the `name` field is `name!`. The `name` type has // two fields: `first` and the `last`. Both of the fields are of type `String!`. service /graphql on new graphql:Listener(9090) { // This resource method represents the `quote` field under the `profile` object. resource function get profile/quote() returns string { return "I am the one who knocks!"; } // This resource method represents the `first` field under the `name` object type. The `name` // field in the `profile` object is of type `name!`. resource function get profile/name/first() returns string { return "Walter"; } // This resource method represents the `last` field under the `name` object type. The `name` // field in the `profile` object is of type `name!`. resource function get profile/name/last() returns string { return "White"; } } ================================================ FILE: examples/graphql-hierarchical-resource-paths/graphql_hierarchical_resource_paths.client.out ================================================ $ curl -X POST -H "Content-type: application/json" -d '{ "query": "{ profile { quote name { first } } }" }' 'http://localhost:9090/graphql' {"data":{"profile":{"quote":"I am the one who knocks!", "name":{"first":"Walter"}}}} ================================================ FILE: examples/graphql-hierarchical-resource-paths/graphql_hierarchical_resource_paths.graphql ================================================ { profile { quote name { first } } } ================================================ FILE: examples/graphql-hierarchical-resource-paths/graphql_hierarchical_resource_paths.md ================================================ # GraphQL service - Hierarchical resource paths The Ballerina `graphql` module allows using hierarchical resource paths in the GraphQL resources. When hierarchical resource paths are used, a GraphQL output object type is created for each intermediate path segment with the same name. Every sub-path under a path segment is added as a field of the created type. Hierarchical paths can be used when there is no need to define the GraphQL output object types explicitly. ::: code graphql_hierarchical_resource_paths.bal ::: Run the service by executing the following command. ::: out graphql_hierarchical_resource_paths.server.out ::: Send the following document to the GraphQL endpoint to test the service. ::: code graphql_hierarchical_resource_paths.graphql ::: To send the document, execute the following cURL command in a separate terminal. ::: out graphql_hierarchical_resource_paths.client.out ::: >**Tip:** You can invoke the above service via the [GraphQL client](/learn/by-example/graphql-client-query-endpoint/). ## Related links - [`graphql` module - API documentation](https://lib.ballerina.io/ballerina/graphql/latest) - [GraphQL hierarchical resource paths - Specification](/spec/graphql/#333-hierarchical-resource-path) ================================================ FILE: examples/graphql-hierarchical-resource-paths/graphql_hierarchical_resource_paths.metatags ================================================ description: This example demonstrates using hierarchical resource paths in GraphQL services. keywords: ballerina, ballerina by example, bbe, graphql, service, hierarchical resource paths ================================================ FILE: examples/graphql-hierarchical-resource-paths/graphql_hierarchical_resource_paths.server.out ================================================ $ bal run graphql_hierarchical_resource_paths.bal ================================================ FILE: examples/graphql-id-scalar-type/graphql_id_scalar_type.bal ================================================ import ballerina/graphql; // Defines a `record` type to use as an object in the GraphQL service. public type User readonly & record {| @graphql:ID int id; string name; int age; |}; // Defines an in-memory table to store the users. table key(id) users = table [ {id: 1, name: "Walter White", age: 50}, {id: 2, name: "Jesse Pinkman", age: 25} ]; service /graphql on new graphql:Listener(9090) { // Returns the user for the given `id`. resource function get user(@graphql:ID int id) returns User|error { if users.hasKey(id) { return users.get(id); } return error(string `User with the ${id} not found`); } } ================================================ FILE: examples/graphql-id-scalar-type/graphql_id_scalar_type.client.out ================================================ $ curl -X POST -H "Content-type: application/json" -d '{ "query": "{ user(id: 1){ id name } }" }' 'http://localhost:9090/graphql' {"data":{"user":{"id":1, "name":"Walter White"}}} ================================================ FILE: examples/graphql-id-scalar-type/graphql_id_scalar_type.graphql ================================================ { user(id: 1) { id name } } ================================================ FILE: examples/graphql-id-scalar-type/graphql_id_scalar_type.md ================================================ # GraphQL service - ID scalar type The Ballerina `graphql` module provides support for the `ID` scalar type in GraphQL. The `ID` type is used to represent unique identifiers within a GraphQL schema. It is a built-in scalar type that can be utilized to define input parameters for GraphQL fields. Applying the `@graphql:ID` annotation to specific fields indicates that they correspond to the GraphQL `ID` scalar type. Consequently, the generated schema will display the field type as `ID` regardless of the actual type of the field. ::: code graphql_id_scalar_type.bal ::: Run the service by executing the following command. ::: out graphql_id_scalar_type.server.out ::: Send the following document to the GraphQL endpoint to test the service. ::: code graphql_id_scalar_type.graphql ::: To send the document, execute the following cURL command in a separate terminal. ::: out graphql_id_scalar_type.client.out ::: >**Tip:** You can invoke the above service via the [GraphQL client](/learn/by-example/graphql-client-query-endpoint/). ## Related links - [`graphql` module - API documentation](https://lib.ballerina.io/ballerina/graphql/latest) - [GraphQL ID scalar type - Specification](/spec/graphql/#415-id) ================================================ FILE: examples/graphql-id-scalar-type/graphql_id_scalar_type.metatags ================================================ description: This example demonstrates using the GraphQL ID scalar type as a unique identifier. keywords: ballerina, ballerina by example, bbe, graphql, scalar, id ================================================ FILE: examples/graphql-id-scalar-type/graphql_id_scalar_type.server.out ================================================ $ bal run graphql_id_scalar.bal ================================================ FILE: examples/graphql-input-constraint-validation/graphql_input_constraint_validation.bal ================================================ import ballerina/constraint; import ballerina/graphql; public type Profile record {| // Define constraints for the fields @constraint:String { maxLength: 5 } string name; @constraint:Int { minValue: 0 } int age; @constraint:Float { maxValue: 3.0 } float height; |}; service /graphql on new graphql:Listener(9090) { resource function get name(Profile profile) returns string { return profile.name; } } ================================================ FILE: examples/graphql-input-constraint-validation/graphql_input_constraint_validation.client.out ================================================ curl -X POST -H "Content-type: application/json" -d '{ "query": "query{ name(profile:{name:\"Harry Potter\", age: -4, height:6})}" }' 'http://localhost:9090/graphql' {"errors":[{"message":"Input validation failed in the field \"name\": Validation failed for '$.age:minValue','$.height:maxValue','$.name:maxLength' constraint(s).", "locations":[{"line":1, "column":8}], "path":["name"]}], "data":null} ================================================ FILE: examples/graphql-input-constraint-validation/graphql_input_constraint_validation.graphql ================================================ { name(profile: {name: "Harry Potter", age: -4, height: 6}) } ================================================ FILE: examples/graphql-input-constraint-validation/graphql_input_constraint_validation.md ================================================ # GraphQL service - Input constraint validation GraphQL input constraint validation allows you to define and enforce constraints on input arguments. These constraints ensure that the provided input values meet certain criteria before they are processed. The input constraints can be configured using the `@constraint` annotation provided by the Ballerina constraint package. The constraint validation can be enabled or disabled using the `validation` field in the `graphql:ServiceConfig`. By default, the validation field is set to `true`. ::: code graphql_input_constraint_validation.bal ::: Run the service by executing the following command. ::: out graphql_input_constraint_validation.server.out ::: Send the following document to the GraphQL endpoint to test the service. ::: code graphql_input_constraint_validation.graphql ::: To send the document, execute the following cURL command in a separate terminal. ::: out graphql_input_constraint_validation.client.out ::: >**Tip:** You can invoke the above service via the [GraphQL client](/learn/by-example/graphql-client-query-endpoint/). ## Related links - [Constraint annotation - API documentation](https://lib.ballerina.io/ballerina/constraint/latest#Annotations) - [GraphQL constraint config - Specification](/spec/graphql/#718-constraint-configurations) - [`constraint` module - API documentation](https://lib.ballerina.io/ballerina/constraint/latest) - [`graphql` module - API documentation](https://lib.ballerina.io/ballerina/graphql/latest) ================================================ FILE: examples/graphql-input-constraint-validation/graphql_input_constraint_validation.metatags ================================================ description: This example demonstrates defining and enforcing constraints on the input arguments of a GraphQL service. keywords: ballerina, ballerina by example, bbe, graphql, graphql input constraint validation, graphql constraint validation ================================================ FILE: examples/graphql-input-constraint-validation/graphql_input_constraint_validation.server.out ================================================ $ bal run graphql_input_constraint_validation.bal ================================================ FILE: examples/graphql-input-objects/graphql_input_objects.bal ================================================ import ballerina/graphql; // Defines the `NewProfile` record type to use as an input object. type NewProfile record {| string name; int age; |}; // Defines the `Profile` record type to use as an output object. type Profile readonly & record {| *NewProfile; int id; |}; // Defines an in-memory table to store the Profiles. table key(id) profiles = table []; service /graphql on new graphql:Listener(9090) { // This remote method (`addProfile`) has an input argument `newProfile` of type `NewProfile!`. // This `NewProfile` record type will be mapped to an `INPUT_OBJECT` type in the generated // GraphQL schema. This method will take the next ID from the table and adds it to the `Profile` // record. remote function addProfile(NewProfile newProfile) returns Profile { Profile profile = {id: profiles.nextKey(), ...newProfile}; profiles.add(profile); return profile; } // Query resolver to retrive all the profiles. A Ballerina GraphQL resolver can return a table // directly. resource function get profiles() returns table { return profiles; } } ================================================ FILE: examples/graphql-input-objects/graphql_input_objects.client.out ================================================ $ curl -X POST -H "Content-type: application/json" -d '{ "query": "mutation { addProfile(newProfile: { name: \"Walter White\", age: 50 }) { id } }" }' 'http://localhost:9090/graphql' {"data":{"addProfile":{"id":0}}} ================================================ FILE: examples/graphql-input-objects/graphql_input_objects.graphql ================================================ mutation { addProfile(newProfile: { name: "Walter White", age: 50 }) { id } } ================================================ FILE: examples/graphql-input-objects/graphql_input_objects.md ================================================ # GraphQL service - Input objects The Ballerina `graphql` module allows defining GraphQL input objects in a `graphql:Service` using Ballerina records. To define a GraphQL input object, define a record type in Ballerina and use it as an input type in a `resource` or a `remote` method inside a `graphql:Service`. Use GraphQL input objects to define non-primitive, structured input arguments in a GraphQL API. ::: code graphql_input_objects.bal ::: Run the service by executing the following command. ::: out graphql_input_objects.server.out ::: Send the following document to the GraphQL endpoint to test the service. ::: code graphql_input_objects.graphql ::: To send the document, execute the following cURL command in a separate terminal. ::: out graphql_input_objects.client.out ::: >**Tip:** You can invoke the above service via the [GraphQL client](/learn/by-example/graphql-client-query-endpoint/). ## Related links - [`graphql` module - API documentation](https://lib.ballerina.io/ballerina/graphql/latest) - [GraphQL input objects - Specification](/spec/graphql/#452-input-objects) ================================================ FILE: examples/graphql-input-objects/graphql_input_objects.metatags ================================================ description: This example demonstrates defining input objects in a GraphQL service using Ballerina records. keywords: ballerina, ballerina by example, bbe, graphql, input objects ================================================ FILE: examples/graphql-input-objects/graphql_input_objects.server.out ================================================ $ bal run graphql_input_objects.bal ================================================ FILE: examples/graphql-input-types/graphql_input_types.bal ================================================ import ballerina/graphql; service /graphql on new graphql:Listener(9090) { // In the generated schema of this GraphQL service, the `greeting` field of the `Query` type has // an input value `name`, which is of the type `String!`. resource function get greeting(string name) returns string { return string `Hello, ${name}`; } } ================================================ FILE: examples/graphql-input-types/graphql_input_types.client.out ================================================ $ curl -X POST -H "Content-type: application/json" -d '{ "query": "{ greeting(name: \"Walter\") }" }' 'http://localhost:9090/graphql' {"data":{"greeting":"Hello, Walter"}} ================================================ FILE: examples/graphql-input-types/graphql_input_types.graphql ================================================ { greeting(name: "Walter") } ================================================ FILE: examples/graphql-input-types/graphql_input_types.md ================================================ # GraphQL service - Input types The Ballerina `graphql` module allows defining input parameters for the GraphQL fields. To define input parameters, add the desired input parameters in the `resource` and `remote` methods in a `graphql:Service` and the subsequent service types. ::: code graphql_input_types.bal ::: Run the service by executing the following command. ::: out graphql_input_types.server.out ::: Send the following document to the GraphQL endpoint to test the service. ::: code graphql_input_types.graphql ::: To send the document, execute the following cURL command in a separate terminal. ::: out graphql_input_types.client.out ::: >**Tip:** You can invoke the above service via the [GraphQL client](/learn/by-example/graphql-client-query-endpoint/). ## Related links - [`graphql` module - API documentation](https://lib.ballerina.io/ballerina/graphql/latest) - [GraphQL input types - Specification](/spec/graphql/#45-input-types) ================================================ FILE: examples/graphql-input-types/graphql_input_types.metatags ================================================ description: This example demonstrates defining input parameters for the fields of a GraphQL service. keywords: ballerina, ballerina by example, bbe, graphql, input types ================================================ FILE: examples/graphql-input-types/graphql_input_types.server.out ================================================ $ bal run graphql_input_types.bal ================================================ FILE: examples/graphql-interceptor-configurations/graphql_interceptor_configurations.bal ================================================ import ballerina/graphql; import ballerina/log; import ballerina/time; // Configure the behavior of the interceptor. @graphql:InterceptorConfig { // Change the scope of the interceptor. global: false } readonly service class LogExecutionTime { *graphql:Interceptor; isolated remote function execute(graphql:Context context, graphql:Field 'field) returns anydata|error { // Access the current execution field name using the `graphql:Field` object. string fieldName = 'field.getName(); // Get execution start time. time:Utc startUtc = time:utcNow(); // Invoke the next interceptor or resolver. var data = context.resolve('field); // Get execution end time. time:Utc endUtc = time:utcNow(); // Calculate the execution time. time:Seconds executionTime = time:utcDiffSeconds(endUtc, startUtc); // Log the execution time. log:printInfo(string `execution time of the "${fieldName}" field: ${executionTime}s`); // Returns the execution result of the next interceptor or the resolver. return data; } } // Define the type Name. public type Name record {| string first; string last; |}; @graphql:ServiceConfig { // Insert the service interceptor. interceptors: new LogExecutionTime() } service /graphql on new graphql:Listener(9090) { isolated resource function get name() returns Name { return { first: "GraphQL", last: "Interceptors" }; } } ================================================ FILE: examples/graphql-interceptor-configurations/graphql_interceptor_configurations.client.out ================================================ $ curl -X POST -H "Content-type: application/json" -d '{ "query": "{ name { first last } }"}' 'http://localhost:9090/graphql' {"data":{"name": { "first":"GraphQL", "last":"Interceptors"}} ================================================ FILE: examples/graphql-interceptor-configurations/graphql_interceptor_configurations.graphql ================================================ { name { first last } } ================================================ FILE: examples/graphql-interceptor-configurations/graphql_interceptor_configurations.md ================================================ # GraphQL service - Interceptor configurations A GraphQL interceptor can be configured to change the behavior of an interceptor. It can be configured via the `graphql:InterceptorConfig` annotation. The `graphql:InterceptorConfig` includes the `global` field, which accepts a boolean value to define the scope of the interceptor. - If the `global` field is set to `true`, the interceptor will be applied to each field and subfield of the service. - If the `global` field is set to `false`, the interceptor will be applied only to the fields of the service, but not to the subfields of the service. >**Info:** By default, the `global` flag is set to `true`. Use `global: false` to apply the interceptor functionality only to an entry point of the GraphQL service. The scope configuration is applied only to the GraphQL `service interceptors`. ::: code graphql_interceptor_configurations.bal ::: Run the service by executing the following command. ::: out graphql_interceptor_configurations.server.out ::: Send the following document to the GraphQL endpoint to test the service. ::: code graphql_interceptor_configurations.graphql ::: To send the document, execute the following cURL command in a separate terminal. ::: out graphql_interceptor_configurations.client.out ::: >**Tip:** You can invoke the above service via the [GraphQL client](/learn/by-example/graphql-client-query-endpoint/). ## Related links - [`graphql:InterceptorConfig` annotation - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#InterceptorConfig) - [GraphQL interceptor configuration - Specification](/spec/graphql/#73-interceptor-configuration) - [GraphQL interceptors - Specification](/spec/graphql/#103-interceptors) ================================================ FILE: examples/graphql-interceptor-configurations/graphql_interceptor_configurations.metatags ================================================ description: This example demonstrates configuring the behavior of a GraphQL interceptor. keywords: ballerina, ballerina by example, bbe, graphql, service, interceptor ================================================ FILE: examples/graphql-interceptor-configurations/graphql_interceptor_configurations.server.out ================================================ $ bal run graphql_interceptor_configurations.bal # when executing the query, following statements are logged in the terminal. time = 2023-04-10T12:11:01.263+05:30 level = INFO module = "" message = "execution time of the \"name\" field: 0.022773s" ================================================ FILE: examples/graphql-interfaces/graphql_interfaces.bal ================================================ import ballerina/graphql; // Defines the interface `Profile` using a `distinct` `service` object. type Profile distinct service object { // Defines the field `name` as a resource method definition. resource function get name() returns string; }; // Defines the `Teacher` class implementing the `Profile` interface. distinct service class Teacher { // This denotes that this object implements the `Profile` interface. *Profile; private final string name; private final string subject; function init(string name, string subject) { self.name = name; self.subject = subject; } // Since this object implements the `Profile` interface, this object must implement the fields // of the `Profile` interface. resource function get name() returns string { return self.name; } // Adds an additional field `subject` to the `Teacher` class. resource function get subject() returns string { return self.subject; } } // Defines another class implementing the `Profile` interface. distinct service class Student { *Profile; private final string name; function init(string name) { self.name = name; } resource function get name() returns string { return "Jesse Pinkman"; } } service /graphql on new graphql:Listener(9090) { // Returns the `Profile[]` type from a GraphQL resolver. The `Profile` type is identified as an // interface. resource function get profiles() returns Profile[] { return [new Teacher("Walter White", "Chemistry"), new Student("Jesse Pinkman")]; } } ================================================ FILE: examples/graphql-interfaces/graphql_interfaces.client.out ================================================ $ curl -X POST -H "Content-type: application/json" -d '{ "query": "{ profiles { name ...on Teacher { subject }}}" }' 'http://localhost:9090/graphql' {"data":{"profiles":[{"name":"Walter White", "subject":"Chemistry"}, {"name":"Jesse Pinkman"}]}} ================================================ FILE: examples/graphql-interfaces/graphql_interfaces.graphql ================================================ { profiles { name ... on Teacher { subject } } } ================================================ FILE: examples/graphql-interfaces/graphql_interfaces.md ================================================ # GraphQL service - Interfaces The Ballerina `graphql` module allows defining GraphQL interface types. An interface specifies a set of fields that multiple object types can include. In Ballerina, interfaces are defined using `distinct` `service` objects and the fields of the interfaces are defined as resource method definitions. Objects that are implementing the interfaces must implement the `resource` methods defined in the `service` objects. The Ballerina type inclusion includes the interface type to an object type. Use an interface to return a type that consists of a set of possible types with common fields. ::: code graphql_interfaces.bal ::: Run the service by executing the following command. ::: out graphql_interfaces.server.out ::: Send the following document to the GraphQL endpoint to test the service. ::: code graphql_interfaces.graphql ::: To send the document, execute the following cURL command in a separate terminal. ::: out graphql_interfaces.client.out ::: >**Tip:** You can invoke the above service via the [GraphQL client](/learn/by-example/graphql-client-query-endpoint/). ## Related links - [`graphql` module - API documentation](https://lib.ballerina.io/ballerina/graphql/latest) - [GraphQL interfaces - Specification](/spec/graphql/#46-interfaces) ================================================ FILE: examples/graphql-interfaces/graphql_interfaces.metatags ================================================ description: This example demonstrates defining GraphQL interface types. keywords: ballerina, ballerina by example, bbe, graphql, interfaces ================================================ FILE: examples/graphql-interfaces/graphql_interfaces.server.out ================================================ $ bal run graphql_interfaces.bal ================================================ FILE: examples/graphql-interfaces-implementing-interfaces/graphql_interfaces_implementing_interfaces.bal ================================================ import ballerina/graphql; // Defines the `Profile` interface using a `distinct` `service` object. type Profile distinct service object { // Defines the `name` field as a resource method definition. resource function get name() returns string; }; // Defines another `Teacher` interface, which implements the `Profile` interface. type Teacher distinct service object { // Denotes that this interface implements the `Profile` interface. *Profile; // Adds an additional field to the `Teacher` interface. resource function get school() returns string; }; // Defines the `HighSchoolTeacher` class implementing the `Teacher` interface. distinct service class HighSchoolTeacher { // Denotes that this object implements the `Teacher` interface. Since this object implements // the `Teacher` interface and the `Teacher` interface implements the `Profile` interface, this // object must implement the fields from both interfaces. *Teacher; private final string name; private final string school; private final string subject; function init(string name, string school, string subject) { self.name = name; self.school = school; self.subject = subject; } // Implements the `name` field from the `Profile` interface. resource function get name() returns string { return self.name; } // Implements the `school` field from the `Teacher` interface. resource function get school() returns string { return self.school; } // Adds an additional `subject` field to the `HighSchoolTeacher` class. resource function get subject() returns string { return self.subject; } } service /graphql on new graphql:Listener(9090) { // Returns the `Profile` type from a GraphQL resolver. The `Profile` type is identified as an // interface. resource function get profile() returns Profile { return new HighSchoolTeacher("Walter White", "J. P. Wynne", "Chemistry"); } } ================================================ FILE: examples/graphql-interfaces-implementing-interfaces/graphql_interfaces_implementing_interfaces.client.out ================================================ $ curl -X POST -H "Content-type: application/json" -d '{ "query": "{ profile { name ...on Teacher { school ...on HighSchoolTeacher { subject }}}}" }' 'http://localhost:9090/graphql' {"data":{"profile":{"name":"Walter White", "school":"J. P. Wynne", "subject":"Chemistry"}}} ================================================ FILE: examples/graphql-interfaces-implementing-interfaces/graphql_interfaces_implementing_interfaces.graphql ================================================ { profile { name ... on Teacher { school ... on HighSchoolTeacher { subject } } } } ================================================ FILE: examples/graphql-interfaces-implementing-interfaces/graphql_interfaces_implementing_interfaces.md ================================================ # GraphQL service - Interfaces implementing interfaces The Ballerina `graphql` module allows defining GraphQL interface types that can implement other interfaces. A `distinct` `service` object must be used to define an interface. The Ballerina type-inclusion is used to define interfaces that implement other interfaces. This can be used in GraphQL schemas to define higher-order interfaces that share common functionalities in the application logic. ::: code graphql_interfaces_implementing_interfaces.bal ::: Run the service by executing the following command. ::: out graphql_interfaces_implementing_interfaces.server.out ::: Send the following document to the GraphQL endpoint to test the service. ::: code graphql_interfaces_implementing_interfaces.graphql ::: To send the document, execute the following cURL command in a separate terminal. ::: out graphql_interfaces_implementing_interfaces.client.out ::: >**Tip:** You can invoke the above service via the [GraphQL client](/learn/by-example/graphql-client-query-endpoint/). ## Related links - [`graphql` module - API documentation](https://lib.ballerina.io/ballerina/graphql/latest) - [GraphQL interfaces implementing interfaces - Specification](/spec/graphql/#461-interfaces-implementing-interfaces) ================================================ FILE: examples/graphql-interfaces-implementing-interfaces/graphql_interfaces_implementing_interfaces.metatags ================================================ description: This example demonstrates defining GraphQL interface types that can implement other interfaces. keywords: ballerina, ballerina by example, bbe, graphql, interfaces, interfaces implementing interfaces ================================================ FILE: examples/graphql-interfaces-implementing-interfaces/graphql_interfaces_implementing_interfaces.server.out ================================================ $ bal run graphql_interfaces_implementing_interfaces.bal ================================================ FILE: examples/graphql-mutations/graphql_mutations.bal ================================================ import ballerina/graphql; // Defines a `record` type to use as an object in the GraphQL service. type Profile readonly & record {| int id; string name; int age; |}; // Defines an in-memory table to store the profiles. table key(id) profiles = table [ {id: 1, name: "Walter White", age: 50}, {id: 2, name: "Jesse Pinkman", age: 25} ]; service /graphql on new graphql:Listener(9090) { // A resource method represents a field in the root `Query` operation. resource function get profile(int id) returns Profile { return profiles.get(id); } // A `remote` method represents a field in the root `Mutation` operation. This `remote` method // is used to update the name for the given profile ID and returns the updated `Profile` value. // If the ID is not found, this will return an error. remote function updateName(int id, string name) returns Profile|error { if profiles.hasKey(id) { Profile profile = profiles.remove(id); Profile updatedProfile = { id: profile.id, name: name, age: profile.age }; profiles.put(updatedProfile); return updatedProfile; } return error(string `Profile with ID "${id}" not found`); } } ================================================ FILE: examples/graphql-mutations/graphql_mutations.client.out ================================================ $ curl -X POST -H "Content-type: application/json" -d '{ "query": "mutation { updateName(id: 1, name: \"Mr. Lambert\") { id name } }" }' 'http://localhost:9090/graphql' {"data":{"updateName":{"id":1, "name":"Mr. Lambert"}}} ================================================ FILE: examples/graphql-mutations/graphql_mutations.graphql ================================================ mutation { updateName(id: 1, name: "Mr. Lambert") { id name } } ================================================ FILE: examples/graphql-mutations/graphql_mutations.md ================================================ # GraphQL service - Mutations The Ballerina `graphql` module allows defining GraphQL `Mutation` operations. A `remote` method inside a `graphql:Service` represents a field in the root `Mutation` object type. Therefore, if a `remote` method is present inside the `graphql:Service`, the auto-generated schema will have the `Mutation` type. Each `remote` method in the service will be added as a field of the `Mutation` type. The field name will be the `remote` method name and the field type will be the return type of the `remote` method. Use the `Mutation` operation when performing any side-effects on the underlying data system. >**Note:** GraphQL mutations are actions that are expected to mutate the state of the server. Ballerina uses `remote` methods to handle such cases. Therefore, these `remote` methods are usually named using verbs. ::: code graphql_mutations.bal ::: Run the service by executing the following command. ::: out graphql_mutations.server.out ::: Then, send the following document to update the name. ::: code graphql_mutations.graphql ::: To send the document, execute the following cURL command. ::: out graphql_mutations.client.out ::: >**Tip:** You can invoke the above service via the [GraphQL client](/learn/by-example/graphql-client-query-endpoint/). ## Related links - [`graphql` module - API documentation](https://lib.ballerina.io/ballerina/graphql/latest) - [GraphQL `Mutation` type - Specification](/spec/graphql/#312-the-mutation-type) ================================================ FILE: examples/graphql-mutations/graphql_mutations.metatags ================================================ description: This example demonstrates defining a GraphQL mutation operation. keywords: ballerina, ballerina by example, bbe, graphql, mutation ================================================ FILE: examples/graphql-mutations/graphql_mutations.server.out ================================================ $ bal run graphql_mutations.bal ================================================ FILE: examples/graphql-returning-record-values/graphql_returning_record_values.bal ================================================ import ballerina/graphql; // Define the custom record types for the returning data. public type Profile record {| string name; int age; Address address; |}; public type Address record {| string number; string street; string city; |}; service /graphql on new graphql:Listener(9090) { // Ballerina GraphQL resolvers can return `record` values. The record will be mapped to a // GraphQL output object type in the generated GraphQL schema with the same name and fields. resource function get profile() returns Profile { return { name: "Walter White", age: 51, address: { number: "308", street: "Negra Arroyo Lane", city: "Albuquerque" } }; } } ================================================ FILE: examples/graphql-returning-record-values/graphql_returning_record_values.client.out ================================================ $ curl -X POST -H "Content-type: application/json" -d '{ "query": "{ profile { name, address { city } } }" }' 'http://localhost:9090/graphql' {"data":{"profile":{"name":"Walter White", "address":{"city":"Albuquerque"}}}} ================================================ FILE: examples/graphql-returning-record-values/graphql_returning_record_values.graphql ================================================ { profile { name, address { city } } } ================================================ FILE: examples/graphql-returning-record-values/graphql_returning_record_values.md ================================================ # GraphQL service - Record as output object The Ballerina `graphql` module allows returning `record` types from the `resource` or `remote` methods of the `graphql:Service`. These `record` types are mapped to GraphQL output object types in the GraphQL schema in which the type name and the field names are mapped one-to-one from Ballerina to GraphQL. Use a `record` type to represent a GraphQL output object type only when all fields of that object type do not have any input arguments or the field resolution does not require any complex logic execution. The `record` type is preferred over the `service` object type in this case as it makes the code more concise. ::: code graphql_returning_record_values.bal ::: Run the service by executing the following command. ::: out graphql_returning_record_values.server.out ::: Send the following document to the GraphQL endpoint to test the service. ::: code graphql_returning_record_values.graphql ::: To send the document, execute the following cURL command in a separate terminal. ::: out graphql_returning_record_values.client.out ::: >**Tip:** You can invoke the above service via the [GraphQL client](/learn/by-example/graphql-client-query-endpoint/). ## Related links - [`graphql` module - API documentation](https://lib.ballerina.io/ballerina/graphql/latest) - [GraphQL record type as object - Specification](/spec/graphql/#421-record-type-as-object) ================================================ FILE: examples/graphql-returning-record-values/graphql_returning_record_values.metatags ================================================ description: This example demonstrates returning record types from the resource or remote methods of a GraphQL service. keywords: ballerina, ballerina by example, bbe, graphql, service, record types ================================================ FILE: examples/graphql-returning-record-values/graphql_returning_record_values.server.out ================================================ $ bal run graphql_returning_record_values.bal ================================================ FILE: examples/graphql-returning-service-objects/graphql_returning_service_objects.bal ================================================ import ballerina/graphql; // Defines a service class to use as an object in the GraphQL service. service class Profile { private final string name; private final int age; function init(string name, int age) { self.name = name; self.age = age; } // Each resource method becomes a field of the `Profile` type. resource function get name() returns string { return self.name; } resource function get age() returns int { return self.age; } resource function get isAdult() returns boolean { return self.age > 21; } } service /graphql on new graphql:Listener(9090) { // This resolver returns a service type, which will be mapped to a GraphQL output object type // named `Profile`. Each resource method in the service type is mapped to a field in the GraphQL // output object type. resource function get profile() returns Profile { return new ("Walter White", 51); } } ================================================ FILE: examples/graphql-returning-service-objects/graphql_returning_service_objects.client.out ================================================ $ curl -X POST -H "Content-type: application/json" -d '{ "query": "{ profile { name isAdult } }" }' 'http://localhost:9090/graphql' {"data":{"profile":{"name":"Walter White", "isAdult":true}}} ================================================ FILE: examples/graphql-returning-service-objects/graphql_returning_service_objects.graphql ================================================ { profile { name isAdult } } ================================================ FILE: examples/graphql-returning-service-objects/graphql_returning_service_objects.md ================================================ # GraphQL service - Service as output object The Ballerina `graphql` module allows returning `service` objects from the `resource` or `remote` methods of the `graphql:Service`. These `service` objects are mapped to GraphQL output object types in the GraphQL schema. Each `resource` method in the returned `service` object becomes a field in the created GraphQL output object type. The `resource` methods in these `service` types can have input parameters. These input parameters are mapped to arguments in the corresponding field. Use a `service` type to represent a GraphQL output object type when a field of that GraphQL output object type has any input arguments. Further, using a `service` type to represent a GraphQL output object type offers the flexibility of organizing complex logic. ::: code graphql_returning_service_objects.bal ::: Run the service by executing the following command. ::: out graphql_returning_service_objects.server.out ::: Send the following document to the GraphQL endpoint to test the service. ::: code graphql_returning_service_objects.graphql ::: To send the document, execute the following cURL command in a separate terminal. ::: out graphql_returning_service_objects.client.out ::: >**Tip:** You can invoke the above service via the [GraphQL client](/learn/by-example/graphql-client-query-endpoint/). ## Related links - [`graphql` module - API documentation](https://lib.ballerina.io/ballerina/graphql/latest) - [GraphQL service type as object - Specification](/spec/graphql/#422-service-type-as-object) ================================================ FILE: examples/graphql-returning-service-objects/graphql_returning_service_objects.metatags ================================================ description: This example demonstrates returning service objects from the resource or remote methods of a GraphQL service. keywords: ballerina, ballerina by example, bbe, graphql, service objects ================================================ FILE: examples/graphql-returning-service-objects/graphql_returning_service_objects.server.out ================================================ $ bal run graphql_returning_service_objects.bal ================================================ FILE: examples/graphql-service-basic-auth-file-user-store/graphql_service_basic_auth_file_user_store.bal ================================================ import ballerina/graphql; type Profile record {| string name; int age; |}; listener graphql:Listener securedEP = new (9090, secureSocket = { key: { certFile: "../resource/path/to/public.crt", keyFile: "../resource/path/to/private.key" } } ); // Basic authentication with the file user store can be enabled by setting // the `graphql:FileUserStoreConfig` configuration. // Authorization is based on scopes, which can be specified in the `scopes` field. @graphql:ServiceConfig { auth: [ { fileUserStoreConfig: {}, scopes: ["admin"] } ] } service /graphql on securedEP { resource function get profile() returns Profile { return { name: "Walter White", age: 50 }; } } ================================================ FILE: examples/graphql-service-basic-auth-file-user-store/graphql_service_basic_auth_file_user_store.md ================================================ # GraphQL service - Basic authentication file user store The `graphql:Service` can be secured with basic authentication and additionally, scopes can be added to enforce authorization. It validates the basic authentication token sent in the `Authorization` header against the provided configurations in the `Config.toml` file. The file stores the usernames and passwords for the authentication and the scopes for the authorization. To engage authentication set the default values to the `fileUserStoreConfig` field and add the `Config.toml` file next to the service file. To enable authorization, set the scopes to the `scopes` field. Both configurations must be given as part of the `@graphql:ServiceConfig` annotation. A `graphql:Error` response is sent to the client when the authentication or authorization fails. Use this to authenticate and authorize requests based on user stores. ::: code graphql_service_basic_auth_file_user_store.bal ::: >**Info:** As a prerequisite to running the service, populate the `Config.toml` file correctly with the user information as shown below. ::: code Config.toml ::: Run the service by executing the command below. ::: out graphql_service_basic_auth_file_user_store.server.out ::: >**Tip:** You can invoke the above service via the [GraphQL client - Basic authentication](/learn/by-example/graphql-client-security-basic-auth/) example. ## Related links - [`graphql:ServiceConfig` annotation - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#ServiceConfig) - [`graphql:FileUserStoreConfigWithScopes` record - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#FileUserStoreConfigWithScopes) - [`auth` module - API documentation](https://lib.ballerina.io/ballerina/auth/latest/) - [GraphQL service basic authentication - file user store - Specification](/spec/graphql/#8111-basic-authentication---file-user-store) ================================================ FILE: examples/graphql-service-basic-auth-file-user-store/graphql_service_basic_auth_file_user_store.metatags ================================================ description: This example demonstrates securing a GraphQL service with basic authentication by validating via a file user store. keywords: ballerina, ballerina by example, bbe, graphql, service, security, auth, basic auth ================================================ FILE: examples/graphql-service-basic-auth-file-user-store/graphql_service_basic_auth_file_user_store.server.out ================================================ $ bal run graphql_service_basic_auth_file_user_store.bal ================================================ FILE: examples/graphql-service-basic-auth-ldap-user-store/graphql_service_basic_auth_ldap_user_store.bal ================================================ import ballerina/graphql; type Profile record {| string name; int age; |}; listener graphql:Listener securedEP = new (9090, secureSocket = { key: { certFile: "../resource/path/to/public.crt", keyFile: "../resource/path/to/private.key" } } ); // Basic authentication with the LDAP user store can be enabled by setting // the `graphql:LdapUserStoreConfig` configuration. // Authorization is based on scopes, which can be specified in the `scopes` field. @graphql:ServiceConfig { auth: [ { ldapUserStoreConfig: { domainName: "avix.lk", connectionUrl: "ldap://localhost:389", connectionName: "cn=admin,dc=avix,dc=lk", connectionPassword: "avix123", userSearchBase: "ou=Users,dc=avix,dc=lk", userEntryObjectClass: "inetOrgPerson", userNameAttribute: "uid", userNameSearchFilter: "(&(objectClass=inetOrgPerson)(uid=?))", userNameListFilter: "(objectClass=inetOrgPerson)", groupSearchBase: ["ou=Groups,dc=avix,dc=lk"], groupEntryObjectClass: "groupOfNames", groupNameAttribute: "cn", groupNameSearchFilter: "(&(objectClass=groupOfNames)(cn=?))", groupNameListFilter: "(objectClass=groupOfNames)", membershipAttribute: "member", userRolesCacheEnabled: true, connectionPoolingEnabled: false, connectionTimeout: 5, readTimeout: 60 }, scopes: ["admin"] } ] } service /graphql on securedEP { resource function get profile() returns Profile { return { name: "Walter White", age: 50 }; } } ================================================ FILE: examples/graphql-service-basic-auth-ldap-user-store/graphql_service_basic_auth_ldap_user_store.md ================================================ # GraphQL service - Basic authentication LDAP user store The `graphql:Service` can be secured with basic authentication and additionally, scopes can be added to enforce authorization. It validates the basic authentication token sent in the `Authorization` header with the LDAP server. This server stores the usernames and passwords for the authentication and the scopes for the authorization. To engage authentication, set the LDAP server configurations to the `ldapUserStoreConfig` field. To enable authorization, set the scopes to the `scopes` field. Both configurations must be given as part of the `@graphql:ServiceConfig` annotation. A `graphql:Error` response is sent to the client when the authentication or authorization fails. Use this to authenticate and authorize requests based on LDAP user stores. ::: code graphql_service_basic_auth_ldap_user_store.bal ::: ## Prerequisites - LDAP server should be up and running. Run the service by executing the command below. ::: out graphql_service_basic_auth_ldap_user_store.server.out ::: >**Tip:** You can invoke the above service via the [GraphQL client - Basic authentication](/learn/by-example/graphql-client-security-basic-auth/) example. ## Related links - [`graphql:ServiceConfig` annotation - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#ServiceConfig) - [`graphql:LdapUserStoreConfigWithScopes` record - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#LdapUserStoreConfigWithScopes) - [`auth` module - API documentation](https://lib.ballerina.io/ballerina/auth/latest/) - [GraphQL service basic authentication - LDAP user store - Specification](/spec/graphql/#8112-basic-authentication---ldap-user-store) ================================================ FILE: examples/graphql-service-basic-auth-ldap-user-store/graphql_service_basic_auth_ldap_user_store.metatags ================================================ description: This example demonstrates securing a GraphQL service with basic authentication by validating via an LDAP user store. keywords: ballerina, ballerina by example, bbe, graphql, service, security, auth, basic auth, ldap ================================================ FILE: examples/graphql-service-basic-auth-ldap-user-store/graphql_service_basic_auth_ldap_user_store.server.out ================================================ $ bal run graphql_service_basic_auth_ldap_user_store.bal ================================================ FILE: examples/graphql-service-cache-invalidation/graphql_service_cache_invalidation.bal ================================================ import ballerina/graphql; // Defines a `record` type to use as an object in the GraphQL service. type User readonly & record {| int id; string name; int age; |}; // Defines an in-memory table to store the profiles. table key(id) users = table [ {id: 1, name: "Walter White", age: 50}, {id: 2, name: "Jesse Pinkman", age: 25} ]; service /graphql on new graphql:Listener(9090) { @graphql:ResourceConfig { cacheConfig: {} } resource function get user(int id) returns User|error { if users.hasKey(id) { return users.get(id); } return error(string `User with the ${id} not found`); } @graphql:ResourceConfig { cacheConfig: {} } resource function get users() returns User[] { return users.toArray(); } // Updates a user. remote function updateUser(graphql:Context context, int id, string name, int age) returns User|error { // `invalidate()` is used to invalidate the cache for the given field. check context.invalidate("user"); if users.hasKey(id) { _ = users.remove(id); User user = {id: id, name: name, age: age}; users.add(user); return user; } return error(string `User with the ${id} not found`); } // Deletes a user. remote function deleteUser(graphql:Context context, int id) returns User|error { // `invalidateAll()` is used to invalidate all the caches in the service. check context.invalidateAll(); if users.hasKey(id) { User user = users.remove(id); return user; } return error(string `User with the ${id} not found`); } } ================================================ FILE: examples/graphql-service-cache-invalidation/graphql_service_cache_invalidation.client.out ================================================ $ curl -X POST -H "Content-type: application/json" -d '{ "query": "mutation updateUser { updateUser(id: 1, name: \"Heisenberg\", age: 52){id name age} }" }' 'http://localhost:9090/graphql' {"data":{"updateUser":{"id":1, "name":"Heisenberg", "age":52}}} ================================================ FILE: examples/graphql-service-cache-invalidation/graphql_service_cache_invalidation.graphql ================================================ mutation updateUser { updateUser(id: 1, name: "Heisenberg" age: 52) { id name age } } ================================================ FILE: examples/graphql-service-cache-invalidation/graphql_service_cache_invalidation.md ================================================ # GraphQL service - Cache invalidation The Ballerina `graphql` module provides functionality for cache invalidation. The `invalidate()` and `invalidateAll()` APIs in the `graphql:Context` can be used to invalidate caches in a `graphql:Service`. The `invalidate()` API supports the cache invalidation of a specific field by providing the full path of the field separated by a full stop(`.`). For example, `invalidate("field.subfield.anotherSubfield")`. Conversely, the `invalidateAll()` API invalidates all caches within the `graphql:Service`. ::: code graphql_service_cache_invalidation.bal ::: Run the service by executing the following command. ::: out graphql_service_cache_invalidation.server.out ::: Then, send the following document to update the user. ::: code graphql_service_cache_invalidation.graphql ::: To send the document, execute the following cURL command. ::: out graphql_service_cache_invalidation.client.out ::: >**Tip:** You can invoke the above service via the [GraphQL client](/learn/by-example/graphql-client-query-endpoint/). ## Related links - [`graphql` module - API documentation](https://lib.ballerina.io/ballerina/graphql/latest) - [GraphQL Cache invalidation - Specification](/spec/graphql/#10713-cache-invalidation) ================================================ FILE: examples/graphql-service-cache-invalidation/graphql_service_cache_invalidation.metatags ================================================ description: This example demonstrates the cache invalidation of a GraphQL service. keywords: ballerina, ballerina by example, graphql, cache, invalidation, server-side caching ================================================ FILE: examples/graphql-service-cache-invalidation/graphql_service_cache_invalidation.server.out ================================================ $ bal run graphql_service_cache_invalidation.bal ================================================ FILE: examples/graphql-service-error-handling/graphql_service_error_handling.1.client.out ================================================ $ curl -X POST -H "Content-type: application/json" -d '{ "query": "{ profile(id: 1) { name age } }" }' 'http://localhost:9090/graphql' {"errors":[{"message":"Error occurred while retrieving age", "locations":[{"line":1, "column":25}], "path":["profile", "age"]}], "data":{"profile":{"name":"Walter White", "age":null}}} ================================================ FILE: examples/graphql-service-error-handling/graphql_service_error_handling.1.graphql ================================================ { profile(id: 1) { name age } } ================================================ FILE: examples/graphql-service-error-handling/graphql_service_error_handling.2.client.out ================================================ $ curl -X POST -H "Content-type: application/json" -d '{ "query": "{ profile(id: 2) { name age } }" }' 'http://localhost:9090/graphql' {"errors":[{"message":"Error occurred while retrieving name", "locations":[{"line":1, "column":20}], "path":["profile", "name"]}], "data":null} ================================================ FILE: examples/graphql-service-error-handling/graphql_service_error_handling.2.graphql ================================================ { profile(id: 2) { name age } } ================================================ FILE: examples/graphql-service-error-handling/graphql_service_error_handling.bal ================================================ import ballerina/graphql; service class Profile { private final int id; private final string name; private final int age; function init(int id, string name, int age) { self.id = id; self.name = name; self.age = age; } // This resource method does not have `nil` as a possible return type, which means the // corresponding GraphQL field type is wrapped by the GraphQL `NON_NULL` type. Therefore, if this // method returns an `error`, the `name` value will become `null` and since it is a `NON_NULL` // field, the value will be propagated to the upper level. This means the corresponding // `profile` field value will become `null`. resource function get name() returns string|error { if self.id == 2 { // Returns a mock error. return error("Error occurred while retrieving name"); } return self.name; } // This resource method has `nil` as a possible return type, which means the corresponding // GraphQL field type is nullable. Therefore, if this field returns an error, the field value // can be `null`. resource function get age() returns int|error? { if self.id == 1 { // Returns a mock error. return error("Error occurred while retrieving age"); } return self.age; } } service /graphql on new graphql:Listener(9090) { // This resource method returns only the `Profile` type, which means the field type is wrapped // by GraphQL `NON_NULL` type. Therefore, if the `null` value is propagated to the `profile` // field (by returning an `error` from the `name` field in the `Profile` object), it will be // propagated further making the `data` field of the response `null`. resource function get profile(int id) returns Profile { return new (id, "Walter White", 50); } } ================================================ FILE: examples/graphql-service-error-handling/graphql_service_error_handling.md ================================================ # GraphQL service - Error handling The Ballerina `graphql` module allows returning `error`s from the `resource` and `remote` methods. When the method returns an `error`, it is added to the GraphQL response under the `errors` field whereas the field value is set to `null` under the `data` field. However, if the return type of the `resource` or the `remote` does not also include `nil`, the GraphQL field type becomes `NON_NULL`. Therefore, the `null` value is propagated to the upper levels until a `null` value is allowed as the field value. This might cause the whole `data` field in the GraphQL response to be `null` in some cases. To avoid this cascading behavior, the return type should include nil as appropriate. ::: code graphql_service_error_handling.bal ::: Run the service by executing the following command. ::: out graphql_service_error_handling.server.out ::: Send the following document to the GraphQL endpoint to test the service. ::: code graphql_service_error_handling.1.graphql ::: To send the document, execute the following cURL command in a separate terminal. ::: out graphql_service_error_handling.1.client.out ::: Check the response to see how both the `errors` and the `data` fields are present in the response. Now, send the following document to the GraphQL endpoint. ::: code graphql_service_error_handling.2.graphql ::: To send the document, execute the following cURL command in a separate terminal. ::: out graphql_service_error_handling.2.client.out ::: Check the response to see how the `data` field is set to null due to propagating the `null` value because the `name` field and the `profile` field are of `NON_NULL` type. >**Tip:** You can invoke the above service via the [GraphQL client](/learn/by-example/graphql-client-query-endpoint/). ## Related links - [`graphql` module - API documentation](https://lib.ballerina.io/ballerina/graphql/latest) - [GraphQL error handling - Specification](/spec/graphql/#62-service-error-handling) ================================================ FILE: examples/graphql-service-error-handling/graphql_service_error_handling.metatags ================================================ description: This example demonstrates handling errors in a Ballerina GraphQL service. keywords: ballerina, ballerina by example, bbe, graphql, service, error handling ================================================ FILE: examples/graphql-service-error-handling/graphql_service_error_handling.server.out ================================================ $ bal run graphql_service_error_handling.bal ================================================ FILE: examples/graphql-service-field-level-caching/graphql_service_field_level_caching.bal ================================================ import ballerina/graphql; // Defines a `record` type to use as an object in the GraphQL service. type User readonly & record {| int id; string name; int age; |}; // Defines an in-memory table to store the users. table key(id) users = table [ {id: 1, name: "Walter White", age: 50}, {id: 2, name: "Jesse Pinkman", age: 25} ]; service /graphql on new graphql:Listener(9090) { // The `cacheConfig` in the `graphql:ResourceConfig` annotation is used to // configure the cache for a specific field in the GraphQL service. // (default: {enabled: true, maxAge: 60, maxSize: 120}) @graphql:ResourceConfig { cacheConfig: {} } resource function get name(int id) returns string|error { if users.hasKey(id) { return users.get(id).name; } return error(string `User with the ${id} not found`); } } ================================================ FILE: examples/graphql-service-field-level-caching/graphql_service_field_level_caching.client.out ================================================ $ curl -X POST -H "Content-type: application/json" -d '{ "query": "{ name(id: 1) }" }' 'http://localhost:9090/graphql' {"data":{"name":"Walter White"}} ================================================ FILE: examples/graphql-service-field-level-caching/graphql_service_field_level_caching.graphql ================================================ { name(id: 1) } ================================================ FILE: examples/graphql-service-field-level-caching/graphql_service_field_level_caching.md ================================================ # GraphQL service - Field-level caching The Ballerina `graphql` module provides the capability to enable GraphQL caching, which can be applied at either the field or operation level. To enable caching at the field level, the `cacheConfig` field in the `graphql:ResourceConfig` annotation can be used on a resource method within a `graphql:Service`. By setting this configuration, caching is enabled for the specified GraphQL field, and it can override the cache configurations set at the operation level. ::: code graphql_service_field_level_caching.bal ::: Run the service by executing the following command. ::: out graphql_service_field_level_caching.server.out ::: Send the following document to the GraphQL endpoint to test the service. ::: code graphql_service_field_level_caching.graphql ::: To send the document, execute the following cURL command. ::: out graphql_service_field_level_caching.client.out ::: >**Tip:** You can invoke the above service via the [GraphQL client](/learn/by-example/graphql-client-query-endpoint/). ## Related links - [`graphql` module - API documentation](https://lib.ballerina.io/ballerina/graphql/latest) - [GraphQL Field-level caching - Specification](/spec/graphql/#10712-field-level-caching) ================================================ FILE: examples/graphql-service-field-level-caching/graphql_service_field_level_caching.metatags ================================================ description: This example demonstrates the field-level caching functionality within a GraphQL service. keywords: ballerina, ballerina by example, graphql, cache, server-side caching ================================================ FILE: examples/graphql-service-field-level-caching/graphql_service_field_level_caching.server.out ================================================ $ bal run graphql_service_field_level_caching.bal ================================================ FILE: examples/graphql-service-field-object/graphql_service_field_object.1.client.out ================================================ $ curl -X POST -H "Content-type: application/json" -H "scope: admin" -d '{ "query": "{ profile { name age } }" }' 'http://localhost:9090/graphql' {"data":{"profile":[{"name":"Walter White", "age":50}, {"name":"Jesse Pinkman", "age":25}]}} ================================================ FILE: examples/graphql-service-field-object/graphql_service_field_object.1.graphql ================================================ { profile { name age } } ================================================ FILE: examples/graphql-service-field-object/graphql_service_field_object.2.client.out ================================================ $ curl -X POST -H "Content-type: application/json" -d '{ "query": "{ profile { name address { city } } }" }' 'http://localhost:9090/graphql' {"data":{"profile":[{"name":"Walter White", "address":{"city":"Albuquerque"}}, {"name":"Jesse Pinkman", "address":{"city":"Albuquerque"}}]}} ================================================ FILE: examples/graphql-service-field-object/graphql_service_field_object.2.graphql ================================================ { profile { name address { city } } } ================================================ FILE: examples/graphql-service-field-object/graphql_service_field_object.bal ================================================ import ballerina/graphql; import ballerina/log; // Define the record types to use as an object in the GraphQL service. // The `address` field is optional and can be removed from the result if it is not needed. type Profile readonly & record {| string name; int age; Address address?; |}; type Address record {| int number; string street; string city; |}; // Define an in-memory table to store the data. table key(name) profileTable = table [ { name: "Walter White", age: 50, address: {number: 308, street: "Negra Arroyo Lane", city: "Albuquerque"} }, { name: "Jesse Pinkman", age: 25, address: {number: 9809, street: "Margo Street", city: "Albuquerque"} } ]; service /graphql on new graphql:Listener(9090) { // If the field is needed, it should be defined as the first parameter of the resolver function. resource function get profile(graphql:Field 'field) returns Profile[]|error { // Check whether the `address` field is included in the subfields. if 'field.getSubfieldNames().indexOf("address") !is int { // Add a log message to check the behavior. log:printInfo("Address field is not queried"); // If the `address` field is not needed, remove the `address` field from the query. return from Profile profile in profileTable select { name: profile.name, age: profile.age }; } // Add a log message to check the behavior. log:printInfo("Querying all the fields"); return from Profile profile in profileTable select { name: profile.name, age: profile.age, address: profile.address }; } } ================================================ FILE: examples/graphql-service-field-object/graphql_service_field_object.md ================================================ # GraphQL service - Field object The Ballerina `graphql` module exposes information about a GraphQL field in a document using the `graphql:Field` object. When the `graphql:field` is needed to be accessed, define it as a parameter of the `resource`/`remote` method that represents a GraphQL field. Use the `graphql:Field` object in scenarios where the information about the fields such as subfield names and field type are needed for optimizing the business logic such as query optimizations. >**Hint:** The `graphql:Field` is defined before the other parameters of a function as a convention. >**Note:** If the `graphql:Field` is defined as a parameter of a resolver function, it will be accessible inside the resolver. Passing it down is not necessary. ::: code graphql_service_field_object.bal ::: Run the service by executing the following command. ::: out graphql_service_field_object.server.1.out ::: Send the following document to the GraphQL endpoint to test the service. ::: code graphql_service_field_object.1.graphql ::: To send the document, execute the following cURL command in a separate terminal. ::: out graphql_service_field_object.1.client.out ::: This will print a log message in the server terminal similar to the following log verifying that the `address` field is not queried. ::: out graphql_service_field_object.server.2.out ::: Now, send the following document of which the `address` field is queried. ::: code graphql_service_field_object.2.graphql ::: To send the document, execute the following cURL command in a separate terminal. ::: out graphql_service_field_object.2.client.out ::: This will print a log message in the server terminal similar to the following log verifying that the `address` field is queried. ::: out graphql_service_field_object.server.3.out ::: >**Tip:** You can invoke the above service via the [GraphQL client](/learn/by-example/graphql-client-query-endpoint/). ## Related links - [`graphql:Field` object - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#Field) - [GraphQL field - Specification](/spec/graphql/#102-field-object) ================================================ FILE: examples/graphql-service-field-object/graphql_service_field_object.metatags ================================================ description: This example demonstrates accessing information of a GraphQL field in a document. keywords: ballerina, ballerina by example, bbe, graphql, field ================================================ FILE: examples/graphql-service-field-object/graphql_service_field_object.server.1.out ================================================ $ bal run graphql_service_field_object.bal ================================================ FILE: examples/graphql-service-field-object/graphql_service_field_object.server.2.out ================================================ time = 2023-01-27T14:32:47.888+05:30 level = INFO module = "" message = "Address field is not queried" ================================================ FILE: examples/graphql-service-field-object/graphql_service_field_object.server.3.out ================================================ time = 2023-01-27T14:33:47.057+05:30 level = INFO module = "" message = "Querying all the fields" ================================================ FILE: examples/graphql-service-interceptors/graphql_service_interceptors.bal ================================================ import ballerina/graphql; import ballerina/log; // Defines an interceptor named `LogInterceptor` using a service class. It cannot have any // `resource`/`remote` methods except the `execute()` remote method. Other methods are allowed. readonly service class LogInterceptor { // Includes the `graphql:Interceptor` service object from the GraphQL package. *graphql:Interceptor; // Implement the `execute()` remote method provided by the `graphql:Interceptor` object. // Within the function, the `graphql:Context` and the `graphql:Field` object can be accessed to // get the request and field-related information. isolated remote function execute(graphql:Context context, graphql:Field 'field) returns anydata|error { // Access the current execution field name using the `graphql:Field` object. string fieldName = 'field.getName(); // This log statement executes before the resolver execution. log:printInfo(string `Field "${fieldName}" execution started!`); // The `context.resolve()` function can be used to invoke the next interceptor. If all the // interceptors were executed, then it invokes the actual resolver function. The function // returns an `anydata` type value that includes the execution result of the next // interceptor or the actual resolver. To call the `context.resolve()` function, the // `graphql:Field` value should be provided as the argument. var data = context.resolve('field); // This log statement executes after the resolver execution. log:printInfo(string `Field "${fieldName}" execution completed!`); // Returns the execution result of the next interceptor or the resolver. return data; } } @graphql:ServiceConfig { // Interceptor instances should be inserted into the `interceptors` array according to the // desired execution order. interceptors: [new LogInterceptor()] } service /graphql on new graphql:Listener(9090) { isolated resource function get name() returns string { log:printInfo("Executing the field \"name\""); return "GraphQL Interceptors"; } } ================================================ FILE: examples/graphql-service-interceptors/graphql_service_interceptors.client.out ================================================ $ curl -X POST -H "Content-type: application/json" -d '{ "query": "{ name }"}' 'http://localhost:9090/graphql' {"data":{"name":"GraphQL Interceptors"}} ================================================ FILE: examples/graphql-service-interceptors/graphql_service_interceptors.graphql ================================================ { name } ================================================ FILE: examples/graphql-service-interceptors/graphql_service_interceptors.md ================================================ # GraphQL service - Service interceptors The `graphql:Service` allows adding interceptors for GraphQL requests to execute custom logic. An interceptor can be defined using a `readonly` class that includes the `graphql:Interceptor` type. The interceptor class must implement the `execute` remote method, which is defined in the `graphql:Interceptor` service object type. They can be passed as an array using the `interceptors` field in the `graphql:ServiceConfig` annotation. The provided interceptors will be executed using the _onion principle_. Use the interceptors to execute custom logic before and after executing the `resource` and `remote` methods that need to be separated from the business logic. >**Note:** A service can have zero or more interceptors. ::: code graphql_service_interceptors.bal ::: Run the service by executing the following command. ::: out graphql_service_interceptors.server.out ::: Send the following document to the GraphQL endpoint to test the service. ::: code graphql_service_interceptors.graphql ::: To send the document, execute the following cURL command in a separate terminal. ::: out graphql_service_interceptors.client.out ::: >**Tip:** You can invoke the above service via the [GraphQL client](/learn/by-example/graphql-client-query-endpoint/). ## Related links - [`graphql:Interceptor` object - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#Interceptor) - [GraphQL service interceptors - Specification](/spec/graphql/#10331-service-interceptors) ================================================ FILE: examples/graphql-service-interceptors/graphql_service_interceptors.metatags ================================================ description: This example demonstrates using interceptors on GraphQL services to execute custom logic. keywords: ballerina, ballerina by example, bbe, graphql, service, interceptors ================================================ FILE: examples/graphql-service-interceptors/graphql_service_interceptors.server.out ================================================ $ bal run graphql_interceptors.bal # when executing the query, following statements are logged in the terminal. time = 2022-11-16T17:09:59.234+05:30 level = INFO module = "" message = "Field \"name\" execution started!" time = 2022-11-16T17:09:59.243+05:30 level = INFO module = "" message = "Executing the field \"name\"" time = 2022-11-16T17:09:59.247+05:30 level = INFO module = "" message = "Field \"name\" execution completed!" ================================================ FILE: examples/graphql-service-jwt-auth/graphql_service_jwt_auth.bal ================================================ import ballerina/graphql; type Profile record {| string name; int age; |}; listener graphql:Listener securedEP = new (9090, secureSocket = { key: { certFile: "../resource/path/to/public.crt", keyFile: "../resource/path/to/private.key" } } ); // The service can be secured with JWT authentication and can be authorized optionally. // JWT authentication can be enabled by setting the `graphql:JwtValidatorConfig` configurations. // Authorization is based on scopes. A scope maps to one or more groups. Authorization can be // enabled by setting the `string|string[]` type configurations for the `scopes` field. @graphql:ServiceConfig { auth: [ { jwtValidatorConfig: { issuer: "wso2", audience: "ballerina", signatureConfig: { certFile: "../resource/path/to/public.crt" }, scopeKey: "scp" }, scopes: ["admin"] } ] } service /graphql on securedEP { resource function get profile() returns Profile { return { name: "Walter White", age: 50 }; } } ================================================ FILE: examples/graphql-service-jwt-auth/graphql_service_jwt_auth.md ================================================ # GraphQL service - JWT authentication The `graphql:Service` can be secured with JWT and additionally, scopes can be added to enforce authorization. It validates the JWT sent in the `Authorization` header against the provided configurations. Ballerina uses the concept of scopes for authorization. A resource declared in a service can be bound to one/more scope(s). The scope can be included in the JWT using a custom claim attribute. That custom claim attribute also can be configured as the `scopeKey`. In the authorization phase, the scopes of the service are compared against the scope included in the JWT for at least one match between the two sets. ::: code graphql_service_jwt_auth.bal ::: Run the service by executing the command below. ::: out graphql_service_jwt_auth.server.out ::: >**Tip:** You can invoke the above service via the [GraphQL client - JWT authentication](/learn/by-example/graphql-client-security-jwt-authentication/) example. ## Related links - [`graphql:ServiceConfig` annotation - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#ServiceConfig) - [`graphql:JwtValidatorConfigWithScopes` record - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#JwtValidatorConfigWithScopes) - [`jwt` module - API documentation](https://lib.ballerina.io/ballerina/jwt/latest/) - [GraphQL service JWT authentication - Specification](/spec/graphql/#8113-jwt-authentication) ================================================ FILE: examples/graphql-service-jwt-auth/graphql_service_jwt_auth.metatags ================================================ description: This example demonstrates securing a GraphQL service with JWT Auth. keywords: ballerina, ballerina by example, bbe, graphql, service, security, auth, jwt auth ================================================ FILE: examples/graphql-service-jwt-auth/graphql_service_jwt_auth.server.out ================================================ $ bal run graphql_service_jwt_auth.bal ================================================ FILE: examples/graphql-service-mutual-ssl/graphql_service_mutual_ssl.bal ================================================ import ballerina/graphql; import ballerina/http; type Profile record {| string name; int age; |}; // A GraphQL listener can be configured to accept new connections that are secured via mutual SSL. listener graphql:Listener securedEP = new (9090, secureSocket = { key: { certFile: "../resource/path/to/public.crt", keyFile: "../resource/path/to/private.key" }, // Enables mutual SSL. mutualSsl: { verifyClient: http:REQUIRE, cert: "../resource/path/to/public.crt" } } ); service /graphql on securedEP { resource function get profile() returns Profile { return { name: "Walter White", age: 50 }; } } ================================================ FILE: examples/graphql-service-mutual-ssl/graphql_service_mutual_ssl.md ================================================ # GraphQL service - Mutual SSL The `graphql:Listener` with mutual SSL (mTLS) enabled in it allows exposing a connection secured with mutual SSL, which is a certificate-based authentication process in which two parties (the client and server) authenticate each other by verifying the digital certificates. It ensures that both parties are assured of each other's identity. The `graphql:Listener` secured with mutual SSL is created by providing the `secureSocket` configurations, which require the word `require` as the `verifyClient`, the server's public certificate as the `certFile`, the server's private key as the `keyFile`, and the client's certificate as the `cert`. Use this to secure the GraphQL connection over mutual SSL. ::: code graphql_service_mutual_ssl.bal ::: Run the service by executing the command below. ::: out graphql_service_mutual_ssl.server.out ::: >**Tip:** You can invoke the above service via the [GraphQL client - Mutual SSL](/learn/by-example/graphql-client-security-mutual-ssl/) example. ## Related links - [`graphql:ListenerConfiguration` record - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#ListenerConfiguration) - [`graphql:ListenerSecureSocket` record - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#ListenerSecureSocket) - [GraphQL service mutual SSL - Specification](/spec/graphql/#8312-mutual-ssl) ================================================ FILE: examples/graphql-service-mutual-ssl/graphql_service_mutual_ssl.metatags ================================================ description: This example demonstrates securing a GraphQL service with mutual SSL. keywords: ballerina, ballerina by example, bbe, graphql, service, security, mutual ssl, ssl protocols, ciphers ================================================ FILE: examples/graphql-service-mutual-ssl/graphql_service_mutual_ssl.server.out ================================================ $ bal run graphql_service_mutual_ssl.bal ================================================ FILE: examples/graphql-service-oauth2/graphql_service_oauth2.bal ================================================ import ballerina/graphql; type Profile record {| string name; int age; |}; listener graphql:Listener securedEP = new (9090, secureSocket = { key: { certFile: "../resource/path/to/public.crt", keyFile: "../resource/path/to/private.key" } } ); // The service can be secured with OAuth2 and by enforcing authorization optionally. // It can be enabled by setting the `graphql:OAuth2IntrospectionConfig` configurations. // Authorization is based on scopes. A scope maps to one or more groups. Authorization can be // enabled by setting the `string|string[]` type configurations for the `scopes` field. @graphql:ServiceConfig { auth: [ { oauth2IntrospectionConfig: { url: "https://localhost:9445/oauth2/introspect", tokenTypeHint: "access_token", scopeKey: "scp", clientConfig: { customHeaders: {"Authorization": "Basic YWRtaW46YWRtaW4="}, secureSocket: { cert: "../resource/path/to/public.crt" } } }, scopes: ["admin"] } ] } service /graphql on securedEP { resource function get profile() returns Profile { return { name: "Walter White", age: 50 }; } } ================================================ FILE: examples/graphql-service-oauth2/graphql_service_oauth2.md ================================================ # GraphQL service - OAuth2 The `graphql:Service` can be secured with OAuth2 and additionally, scopes can be added to enforce fine-grained authorization. It validates the OAuth2 token sent in the `Authorization` header against the provided configurations. This calls the configured introspection endpoint for validation. Ballerina uses the concept of scopes for authorization. A resource declared in a service can be bound to one/more scope(s). The scope can be included in the introspection response using a custom claim attribute. That custom claim attribute also can be configured as the `scopeKey`. In the authorization phase, the scopes of the service are compared against the scope included in the introspection response for at least one match between the two sets. ::: code graphql_service_oauth2.bal ::: ## Prerequisites - An STS endpoint should be up and running. Run the service by executing the command below. ::: out graphql_service_oauth2.server.out ::: >**Tip:** You can invoke the above service via the [GraphQL client - OAuth2 password grant type](/learn/by-example/graphql-client-security-oauth2-password-grant-type/) example. ## Related links - [`graphql:ServiceConfig` annotation - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#ServiceConfig) - [`graphql:OAuth2IntrospectionConfigWithScopes` record - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#OAuth2IntrospectionConfigWithScopes) - [`oauth2` module - API documentation](https://lib.ballerina.io/ballerina/oauth2/latest/) - [GraphQL service OAuth2 - Specification](/spec/graphql/#8114-oauth2) ================================================ FILE: examples/graphql-service-oauth2/graphql_service_oauth2.metatags ================================================ description: This example demonstrates securing a GraphQL service with OAuth2. keywords: ballerina, ballerina by example, bbe, graphql, service, security, auth, oauth2, introspection ================================================ FILE: examples/graphql-service-oauth2/graphql_service_oauth2.server.out ================================================ $ bal run graphql_service_oauth2.bal ================================================ FILE: examples/graphql-service-operation-level-caching/graphql_service_operation_level_caching.bal ================================================ import ballerina/graphql; // Defines a `record` type to use as an object in the GraphQL service. type User readonly & record {| int id; string name; int age; |}; // Defines an in-memory table to store the users. table key(id) users = table [ {id: 1, name: "Walter White", age: 50}, {id: 2, name: "Jesse Pinkman", age: 25} ]; // The `cacheConfig` in the `graphql:ServiceConfig` annotation is used to // configure the cache for the GraphQL service. // (default: {enabled: true, maxAge: 60, maxSize: 120}) @graphql:ServiceConfig { cacheConfig: {} } service /graphql on new graphql:Listener(9090) { resource function get name(int id) returns string|error { if users.hasKey(id) { return users.get(id).name; } return error(string `User with the ${id} not found`); } // The `enabled` field enables/disables the cache for the field. (default: true) @graphql:ResourceConfig { cacheConfig: { enabled: false } } resource function get user(int id) returns User|error { if users.hasKey(id) { return users.get(id); } return error(string `User with the ${id} not found`); } // The `maxAge` field sets the maximum age of the cache in seconds. (default: 60) // The `maxSize` field indicates the maximum capacity of the cache table by entries. // (default: 120) @graphql:ResourceConfig { cacheConfig: { maxAge: 600, maxSize: 100 } } resource function get age(int id) returns int|error { if users.hasKey(id) { return users.get(id).age; } return error(string `User with the ${id} not found`); } } ================================================ FILE: examples/graphql-service-operation-level-caching/graphql_service_operation_level_caching.client.out ================================================ $ curl -X POST -H "Content-type: application/json" -d '{ "query": "{ name(id: 1) }" }' 'http://localhost:9090/graphql' {"data":{"name":"Walter White"}} ================================================ FILE: examples/graphql-service-operation-level-caching/graphql_service_operation_level_caching.graphql ================================================ { name(id: 1) } ================================================ FILE: examples/graphql-service-operation-level-caching/graphql_service_operation_level_caching.md ================================================ # GraphQL service - Operation-level caching The Ballerina `graphql` module provides the capability to enable GraphQL caching. GraphQL caching can be enabled at either the field level or the operation level. To enable caching at the operation level, the `cacheConfig` field in the `graphql:ServiceConfig` annotation can be applied to a service. This configuration will enable caching for all resource paths within the `graphql:Service`. ::: code graphql_service_operation_level_caching.bal ::: Run the service by executing the following command. ::: out graphql_service_operation_level_caching.server.out ::: Send the following document to the GraphQL endpoint to test the service. ::: code graphql_service_operation_level_caching.graphql ::: To send the document, execute the following cURL command. ::: out graphql_service_operation_level_caching.client.out ::: >**Tip:** You can invoke the above service via the [GraphQL client](/learn/by-example/graphql-client-query-endpoint/). ## Related links - [`graphql` module - API documentation](https://lib.ballerina.io/ballerina/graphql/latest) - [GraphQL Operation-level caching - Specification](/spec/graphql/#10711-operation-level-caching) ================================================ FILE: examples/graphql-service-operation-level-caching/graphql_service_operation_level_caching.metatags ================================================ description: This example demonstrates the caching of a GraphQL service at the operation level. keywords: ballerina, ballerina by example, graphql, cache, server-side caching ================================================ FILE: examples/graphql-service-operation-level-caching/graphql_service_operation_level_caching.server.out ================================================ $ bal run graphql_service_operation_level_caching.bal ================================================ FILE: examples/graphql-service-query-complexity/graphql_service_query_complexity.1.graphql ================================================ query { profile(id: 1) { name age } } ================================================ FILE: examples/graphql-service-query-complexity/graphql_service_query_complexity.2.graphql ================================================ query { profile1: profile(id: 1) { name age } profile2: profile(id: 2) { age } } ================================================ FILE: examples/graphql-service-query-complexity/graphql_service_query_complexity.bal ================================================ import ballerina/graphql; // Defines a service class to use as an object in the GraphQL service. service class Profile { private final string name; private final int age; function init(string name, int age) { self.name = name; self.age = age; } @graphql:ResourceConfig { complexity: 1 } resource function get name() returns string { return self.name; } @graphql:ResourceConfig { complexity: 10 } resource function get age() returns int { return self.age; } // Default complexity will be applied resource function get isAdult() returns boolean { return self.age > 21; } } @graphql:ServiceConfig { // The queryComplexityConfig is used to define the values for the maximum query complexity, // default field complexity, and whether to return an error or warning when the maximum // complexity is exceeded. queryComplexityConfig: { maxComplexity: 50, // Maximum complexity allowed for a query defaultFieldComplexity: 2 // Default complexity for a field } } service graphql:Service /graphql on new graphql:Listener(9090) { @graphql:ResourceConfig { complexity: 20 // Assigning a complexity value to the `profile` field } resource function get profile(@graphql:ID int id) returns Profile { // Return a dummy profile object return new ("Walter White", 50); } } ================================================ FILE: examples/graphql-service-query-complexity/graphql_service_query_complexity.client.1.out ================================================ $ curl -X POST -H "Content-type: application/json" -H "scope: admin" -d '{ "query": "{ profile(id: 1) { name age } }" }' 'http://localhost:9090/graphql' {"data":{"profile":{"name":"Walter White", "age":50}}} ================================================ FILE: examples/graphql-service-query-complexity/graphql_service_query_complexity.client.2.out ================================================ curl -X POST -H "Content-type: application/json" -H "scope: admin" -d '{ "query": "{ profile1: profile(id: 1) { name age } profile2: profile(id: 2) { name age } }" }' 'http://localhost:9090/graphql' {"errors":[{"message":"The operation exceeds the maximum query complexity threshold. Maximum allowed complexity: 50, actual complexity: 62", "locations":[{"line":1, "column":1}]}]} ================================================ FILE: examples/graphql-service-query-complexity/graphql_service_query_complexity.md ================================================ # GraphQL service - Query Complexity A `graphql:Service` can be secured by limiting the complexity of the operations that can be executed. This can be done by setting a maximum complexity threshold for a given service. The query complexity is calculated by assigning a complexity value to each field in the GraphQL schema. The complexity of an operation is the sum of the complexity values of the fields in the operation. ::: code graphql_service_query_complexity.bal ::: Run the service by executing the command below. ::: out graphql_service_query_complexity.server.out ::: Send the following document to the GraphQL endpoint to test the service. ::: code graphql_service_query_complexity.1.graphql ::: To send the document, execute the following cURL command in a separate terminal. ::: out graphql_service_query_complexity.client.1.out ::: As shown in the output above, the query is executed without any issues. Now, send the following document to the GraphQL endpoint. ::: code graphql_service_query_complexity.2.graphql ::: To send the document, execute the following cURL command in a separate terminal. ::: out graphql_service_query_complexity.client.2.out ::: This will result in an error as the query complexity exceeds the maximum complexity threshold set for the service. >**Tip:** You can invoke the above service via the [GraphQL client](/learn/by-example/graphql-client-query-endpoint/). ## Related links - [`graphql:ServiceConfig` record - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#ServiceConfig) - [GraphQL `queryComplexityConfiguration` - Specification](/spec/graphql/#7110-query-complexity-configurations) - [GraphQL query complexity validation - Specification](/spec/graphql/#1091-query-complexity-validation) ================================================ FILE: examples/graphql-service-query-complexity/graphql_service_query_complexity.metatags ================================================ description: This example demonstrates securing a GraphQL service query complexity analysis. keywords: ballerina, ballerina by example, bbe, graphql, service, security, query complexity ================================================ FILE: examples/graphql-service-query-complexity/graphql_service_query_complexity.server.out ================================================ $ bal run graphql_service_query_complexity.bal ================================================ FILE: examples/graphql-service-ssl-tls/graphql_service_ssl_tls.bal ================================================ import ballerina/graphql; type Profile record {| string name; int age; |}; listener graphql:Listener securedEP = new (9090, secureSocket = { key: { certFile: "../resource/path/to/public.crt", keyFile: "../resource/path/to/private.key" } } ); service /graphql on securedEP { resource function get profile() returns Profile { return { name: "Walter White", age: 50 }; } } ================================================ FILE: examples/graphql-service-ssl-tls/graphql_service_ssl_tls.md ================================================ # GraphQL service - SSL/TLS The `graphql:Listener` can be configured to communicate through HTTPS by providing a certificate file and a private key file. The certificate and the key can be provided through the `secureSocket` field of the `graphql:ListenerConfiguration`. Use this to secure the communication and data transfer between the server and the client. ::: code graphql_service_ssl_tls.bal ::: Run the service by executing the command below. ::: out graphql_service_ssl_tls.server.out ::: >**Tip:** You can invoke the above service via the [GraphQL client - SSL/TLS](/learn/by-example/graphql-client-security-ssl-tls/) example. ## Related links - [`graphql:ListenerConfiguration` record - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#ListenerConfiguration) - [`graphql:ListenerSecureSocket` record - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#ListenerSecureSocket) - [GraphQL service SSL/TLS - Specification](/spec/graphql/#8311-ssltls) ================================================ FILE: examples/graphql-service-ssl-tls/graphql_service_ssl_tls.metatags ================================================ description: This example demonstrates securing a GraphQL service with SSL. keywords: ballerina, ballerina by example, bbe, graphql, service, security, ssl, tls ================================================ FILE: examples/graphql-service-ssl-tls/graphql_service_ssl_tls.server.out ================================================ $ bal run graphql_service_ssl_tls.bal ================================================ FILE: examples/graphql-service-union-types/graphql_service_union_types.bal ================================================ import ballerina/graphql; // Defines the `SearchResult` union type that includes the `Profile` and the `Address` types. type SearchResult Profile|Address; // Defines the `Profile` class to represent the `Profile` object. distinct service class Profile { private final string name; private final int age; function init(string name, int age) { self.name = name; self.age = age; } // Defines the fields of the `Profile` object. resource function get name() returns string { return self.name; } resource function get age() returns int { return self.age; } } // Defines the `Address` class to represent the `Address` object. distinct service class Address { private final int number; private final string street; private final string city; function init(int number, string street, string city) { self.number = number; self.street = street; self.city = city; } resource function get number() returns int { return self.number; } resource function get street() returns string { return self.street; } resource function get city() returns string { return self.city; } } service /graphql on new graphql:Listener(9090) { // The return type `SearchResult[]` will allow to return an array consisting both `Profile` and // `Address` values. resource function get search(string keyword) returns SearchResult[] { return [new ("Walter White", 50), new (308, "Negra Arroyo Lane", "Albuquerque")]; } } ================================================ FILE: examples/graphql-service-union-types/graphql_service_union_types.client.out ================================================ $ curl -X POST -H "Content-type: application/json" -d '{ "query": "{ search(keyword: \"Walter\") { ... on Profile { name age } ... on Address { number city } } }" }' 'http://localhost:9090/graphql' {"data":{"search":[{"name":"Walter White", "age":50}, {"number":308, "city":"Albuquerque"}]}} ================================================ FILE: examples/graphql-service-union-types/graphql_service_union_types.graphql ================================================ { search(keyword: "Walter") { ... on Profile { name age } ... on Address { number city } } } ================================================ FILE: examples/graphql-service-union-types/graphql_service_union_types.md ================================================ # GraphQL service - Union Types The Ballerina `graphql` module allows defining union types as defined in the GraphQL specification. However, the Ballerina union type that represents a GraphQL union type can only consist of `distinct` `service` classes. This is because the Ballerina type system is structurally-typed, whereas, the GraphQL type system is nominally-typed. The `distinct` types are the only types of Ballerina, which have the similar behavior to nominal types. Use union types when an API requires a type that consists of more than one type. ::: code graphql_service_union_types.bal ::: Run the service by executing the following command. ::: out graphql_service_union_types.server.out ::: Send the following document to the GraphQL endpoint to test the service. ::: code graphql_service_union_types.graphql ::: To send the document, execute the following cURL command in a separate terminal. ::: out graphql_service_union_types.client.out ::: >**Tip:** You can invoke the above service via the [GraphQL client](/learn/by-example/graphql-client-query-endpoint/). ## Related links - [`graphql` module - API documentation](https://lib.ballerina.io/ballerina/graphql/latest) - [GraphQL union types - Specification](/spec/graphql/#43-unions) ================================================ FILE: examples/graphql-service-union-types/graphql_service_union_types.metatags ================================================ description: This example demonstrates defining union types in a Ballerina GraphQL service. keywords: ballerina, ballerina by example, bbe, graphql, service, union type ================================================ FILE: examples/graphql-service-union-types/graphql_service_union_types.server.out ================================================ $ bal run graphql_service_union_types.bal ================================================ FILE: examples/graphql-subscriptions/graphql_subscriptions.bal ================================================ import ballerina/graphql; import ballerina/lang.runtime; import ballerina/random; @graphql:ServiceConfig { graphiql: { enabled: true } } service /graphql on new graphql:Listener(9090) { private final readonly & string[] names = ["Walter White", "Jesse Pinkman", "Saul Goodman"]; resource function get names() returns string[] { return self.names; } // A resource method with the `subscribe` accessor represents a field in the root // `Subscription` operation. It must always return a stream. Since the stream is of type // `string`, the resulting field in the generated GraphQL schema will be of type `String!`. resource function subscribe names() returns stream { // Create a `NameGenerator` object. NameGenerator nameGenerator = new (self.names); // Create a stream using the `NameGenerator` object. stream names = new (nameGenerator); return names; } } // Defines a stream implementor that can be used to create a stream of strings. This will pick a random name from // the list of names and return it with a delay to demonstrate a stream of values. class NameGenerator { private final string[] names; isolated function init(string[] names) { self.names = names; } // The `next` method picks a random name from the list and returns it. public isolated function next() returns record {|string value;|}|error? { // Sleep for 1 second to simulate a delay. runtime:sleep(1); int index = check random:createIntInRange(0, self.names.length()); return {value: self.names[index]}; } } ================================================ FILE: examples/graphql-subscriptions/graphql_subscriptions.client.out ================================================ { "data": { "names": "Walter White" } } ================================================ FILE: examples/graphql-subscriptions/graphql_subscriptions.graphql ================================================ subscription { names } ================================================ FILE: examples/graphql-subscriptions/graphql_subscriptions.md ================================================ # GraphQL service - Subscriptions The Ballerina `graphql` module allows defining GraphQL `Subscription` operations. A resource method with the `subscribe` accessor inside a GraphQL service represents a field in the root `Subscription` type. Therefore, If a resource method with the `subscribe` accessor is present inside the Ballerina GraphQL service, the auto-generated schema will have a `Subscription` type. Each resource method with a `subscribe` accessor in the service is added as a field of the `Subscription` type. The field name will be the resource method name and the field type will be the constraint type of the `stream` returned from the resource method. Not returning a `stream` type from a resource method having a `subscribe` accessor results in a compilation error. Use a subscription operation to monitor small, incremental changes to large objects or to obtain low-latency, real-time updates. >**Note:** GraphQL subscriptions are read-only operations that are used to continuously fetch data from a GraphQL server. They are usually executed against information such as `PersonStatus`, `CurrentLocation`, `TotalDonations`, etc. Ballerina uses `resource` methods to handle such cases. Therefore, these `resource` methods are usually named using nouns with `subscription` accessor. ::: code graphql_subscriptions.bal ::: Run the service by executing the following command. ::: out graphql_subscriptions.server.out ::: Send the following document to the GraphQL endpoint to test the service using a GraphQL client that supports subscriptions to test the service. ::: code graphql_subscriptions.graphql ::: It should result in a response similar to the following. >**Note:** The response will get updated in real-time and can be different due to the random name generation. ::: out graphql_subscriptions.client.out ::: >**Tip:** You can invoke the above service via the [GraphiQL client](/learn/by-example/graphql-graphiql/). ## Related links - [`graphql` module - API documentation](https://lib.ballerina.io/ballerina/graphql/latest) - [GraphQL `Subscription` type - Specification](/spec/graphql/#313-the-subscription-type) ================================================ FILE: examples/graphql-subscriptions/graphql_subscriptions.metatags ================================================ description: This example demonstrates defining a GraphQL subscription operation. keywords: ballerina, ballerina by example, bbe, graphql, service, subscription ================================================ FILE: examples/graphql-subscriptions/graphql_subscriptions.server.out ================================================ $ bal run graphql_subscriptions.bal ================================================ FILE: examples/grpc-client-basic-auth/grpc_client_basic_auth.bal ================================================ import ballerina/io; public function main() returns error? { // Defines the gRPC client to call the APIs secured with basic authentication. HelloWorldClient securedEP = check new("https://localhost:9090", auth = { username: "ldclakmal", password: "ldclakmal@123" }, secureSocket = { cert: "../resource/path/to/public.crt" } ); string result = check securedEP->hello("WSO2"); io:println(result); } ================================================ FILE: examples/grpc-client-basic-auth/grpc_client_basic_auth.md ================================================ # gRPC client - Basic authentication The `grpc:Client` can connect to a service that is secured with basic authentication by enriching the client metadata with the `Authorization: Basic ` header. The username and password for basic authentication can be specified in the `auth` field of the client configuration. ::: code grpc_client_basic_auth.bal ::: Setting up the client is the same as setting up the simple RPC client with additional configurations. For information on implementing the client, see [gRPC client - Simple RPC](/learn/by-example/grpc-client-simple/). ## Prerequisites - Run the gRPC service given in the [gRPC service - Basic authentication file user store](/learn/by-example/grpc-service-basic-auth-file-user-store/) or [gRPC service - Basic authentication LDAP user store](/learn/by-example/grpc-service-basic-auth-ldap-user-store/) examples. Run the client by executing the command below. ::: out grpc_client_basic_auth.out ::: ## Related links - [`grpc:ClientAuthConfig` type - API documentation](https://lib.ballerina.io/ballerina/grpc/latest#ClientAuthConfig) - [gRPC client basic authentication - Specification](/spec/grpc/#5115-client---basic-auth) - [`auth` module - API documentation](https://lib.ballerina.io/ballerina/auth/latest/) ================================================ FILE: examples/grpc-client-basic-auth/grpc_client_basic_auth.metatags ================================================ description: BBE on how to secure gRPC client with Basic Auth in Ballerina. keywords: ballerina, ballerina by example, bbe, grpc, auth, basic auth ================================================ FILE: examples/grpc-client-basic-auth/grpc_client_basic_auth.out ================================================ $ bal run client Hello WSO2 ================================================ FILE: examples/grpc-client-basic-auth/grpc_simple.proto ================================================ // This is the service definition of the simple scenario. syntax = "proto3"; import "google/protobuf/wrappers.proto"; service HelloWorld { rpc hello (google.protobuf.StringValue) returns (google.protobuf.StringValue); } ================================================ FILE: examples/grpc-client-bearer-token-auth/grpc_client_bearer_token_auth.bal ================================================ import ballerina/io; public function main() returns error? { // Defines the gRPC client to call the APIs secured with bearer token authentication. HelloWorldClient securedEP = check new("https://localhost:9090", auth = { token: "56ede317-4511-44b4-8579-a08f094ee8c5" }, secureSocket = { cert: "../resource/path/to/public.crt" } ); string result = check securedEP->hello("WSO2"); io:println(result); } ================================================ FILE: examples/grpc-client-bearer-token-auth/grpc_client_bearer_token_auth.md ================================================ # gRPC client - Bearer token authentication The `grpc:Client` can connect to a service that is secured with bearer token authentication by enriching the client metadata with the `Authorization: Bearer ` header. The bearer token can be specified in the `auth` field of the client configuration. ::: code grpc_client_bearer_token_auth.bal ::: Setting up the client is the same as setting up the simple RPC client with additional configurations. For information on implementing the client, see [gRPC client - Simple RPC](/learn/by-example/grpc-client-simple/). ## Prerequisites - Run a sample secured service with bearer token authentication. Run the client by executing the command below. ::: out grpc_client_bearer_token_auth.out ::: ## Related links - [`grpc:BearerTokenConfig` record - API documentation](https://lib.ballerina.io/ballerina/grpc/latest#BearerTokenConfig) - [gRPC client bearer token authentication - Specification](/spec/grpc/#5116-client---bearer-token-auth) - [`auth` module - API documentation](https://lib.ballerina.io/ballerina/auth/latest/) ================================================ FILE: examples/grpc-client-bearer-token-auth/grpc_client_bearer_token_auth.metatags ================================================ description: BBE on how to secure gRPC client with Bearer token auth in Ballerina. keywords: ballerina, ballerina by example, bbe, grpc, auth, jwt auth ================================================ FILE: examples/grpc-client-bearer-token-auth/grpc_client_bearer_token_auth.out ================================================ $ bal run client Hello WSO2 ================================================ FILE: examples/grpc-client-bearer-token-auth/grpc_simple.proto ================================================ // This is the service definition of the simple scenario. syntax = "proto3"; import "google/protobuf/wrappers.proto"; service HelloWorld { rpc hello (google.protobuf.StringValue) returns (google.protobuf.StringValue); } ================================================ FILE: examples/grpc-client-bidirectional-streaming/grpc_bidirectional_streaming.md ================================================ # gRPC client - Bidirectional streaming RPC A `grpc:Client` is created by providing the endpoint URL of a gRPC server. In the bidirectional streaming scenario, once connected, the client and the service send message streams to each other. In this scenario, the two streams operate independently, and therefore, clients and servers can read and write in any order. Use this to send multiple request messages to a server and get multiple response messages back. ## Generate the service definition 1. Create a new Protocol Buffers definition file `grpc_bidirectional_streaming.proto` and add the service definition below. ::: code grpc_bidirectional_streaming.proto ::: 2. Run the command below in the Ballerina tools distribution for stub generation. ::: code grpc_bidirectional_streaming.out ::: Once you run the command, the `grpc_bidirectional_streaming_pb.bal` file gets generated inside the `stubs` directory. ## Prerequisites - Run the gRPC service given in the [bidirectional streaming RPC service](/learn/by-example/grpc-service-bidirectional-streaming/) example. ## Implement and run the client 1. Create a Ballerina package (e.g., `client`). Delete the `main.bal` file created by default as it is not required for this example. 2. Copy the generated `grpc_bidirectional_streaming_pb.bal` file from the `stubs` directory to the `client` package. 3. Create a new `grpc_bidirectional_streaming_client.bal` file inside the `client` package and add the client implementation below. ::: code grpc_bidirectional_streaming_service_client.bal ::: 4. Run the client by executing the command below. ::: out grpc_bidirectional_streaming_service_client.out ::: ## Related links - [`grpc` module - API documentation](https://lib.ballerina.io/ballerina/grpc/latest) - [gRPC client bidirectional streaming - Specification](/spec/grpc/#44-bidirectional-streaming-rpc) - [gRPC tool](/learn/grpc-tool/) ================================================ FILE: examples/grpc-client-bidirectional-streaming/grpc_bidirectional_streaming.metatags ================================================ description: This Ballerina by Example has a gRPC streaming client for bidirectional streaming RPC. keywords: ballerina, ballerina by example, bbe, grpc, bidirectional streaming, client ================================================ FILE: examples/grpc-client-bidirectional-streaming/grpc_bidirectional_streaming.out ================================================ $ bal grpc --input grpc_bidirectional_streaming.proto --output stubs ================================================ FILE: examples/grpc-client-bidirectional-streaming/grpc_bidirectional_streaming.proto ================================================ // This is the service definition for the bidirectional streaming scenario. syntax = "proto3"; import "google/protobuf/wrappers.proto"; service Chat { rpc chat (stream ChatMessage) returns (stream google.protobuf.StringValue); } message ChatMessage { string name = 1; string message = 2; } ================================================ FILE: examples/grpc-client-bidirectional-streaming/grpc_bidirectional_streaming_service_client.bal ================================================ import ballerina/io; public function main() returns error? { // Creates a gRPC client to interact with the remote server. ChatClient ep = check new ("http://localhost:9090"); // Executes the RPC call and receives the customized streaming client. ChatStreamingClient streamingClient = check ep->chat(); // Reads the server responses in another strand. future f1 = start readResponse(streamingClient); // Sends multiple messages to the server. ChatMessage[] messages = [ {name: "Sam", message: "Hi"}, {name: "Ann", message: "Hey"}, {name: "John", message: "Hello"} ]; foreach ChatMessage msg in messages { check streamingClient->sendChatMessage(msg); } // Once all the messages are sent, the client sends the message to notify the // server about the completion. check streamingClient->complete(); // Waits until all server messages are received. check wait f1; } function readResponse(ChatStreamingClient streamingClient) returns error? { // Receives the server stream response iteratively. string? result = check streamingClient->receiveString(); while !(result is ()) { io:println(result); result = check streamingClient->receiveString(); } } ================================================ FILE: examples/grpc-client-bidirectional-streaming/grpc_bidirectional_streaming_service_client.out ================================================ $ bal run grpc_chat_client Sam: Hi Ann: Hey John: Hello ================================================ FILE: examples/grpc-client-client-streaming/grpc_client_streaming.md ================================================ # gRPC client - Client-side streaming RPC A `grpc:Client` is created by providing the endpoint URL of a gRPC server. In the client streaming scenario, once connected, the client sends a sequence of messages to the remote service and waits for the server to read them and returns a single response message. Use this to send multiple request messages and get a single response message back. ## Generate the service definition 1. Create a new Protocol Buffers definition file `grpc_client_streaming.proto` and add service definition below. ::: code grpc_client_streaming.proto ::: 2. Run the command below in the Ballerina tools distribution for stub generation. ::: out grpc_client_streaming.out ::: Once you run the command, the `grpc_client_streaming_pb.bal` file gets generated inside the stubs directory. ## Prerequisites - Run the gRPC service given in the [client streaming RPC service](/learn/by-example/grpc-service-client-streaming/) example. ## Implement and run the client 1. Create a Ballerina package (e.g., `client`). Delete the `main.bal` file created by default as it is not required for this example. 2. Copy the generated `grpc_client_streaming_pb.bal` file from the `stubs` directory to the `client` package. 3. Create a new `grpc_client_streaming_client.bal` file inside the `client` package and add the client implementation below. ::: code grpc_client_streaming_service_client.bal ::: 4. Run the client by executing the command below. ::: out grpc_client_streaming_service_client.out ::: ## Related links - [`grpc` module - API documentation](https://lib.ballerina.io/ballerina/grpc/latest) - [gRPC client client-side streaming - Specification](/spec/grpc/#43-client-streaming-rpc) - [gRPC tool](/learn/grpc-tool/) ================================================ FILE: examples/grpc-client-client-streaming/grpc_client_streaming.metatags ================================================ description: This Ballerina by Example has a gRPC streaming client for client streaming RPC. keywords: ballerina, ballerina by example, bbe, grpc, client streaming, client ================================================ FILE: examples/grpc-client-client-streaming/grpc_client_streaming.out ================================================ $ bal grpc --input grpc_client_streaming.proto --output stubs ================================================ FILE: examples/grpc-client-client-streaming/grpc_client_streaming.proto ================================================ // This is the service definition for the client streaming scenario. syntax = "proto3"; import "google/protobuf/wrappers.proto"; service HelloWorld { rpc lotsOfGreetings (stream google.protobuf.StringValue) returns (google.protobuf.StringValue); } ================================================ FILE: examples/grpc-client-client-streaming/grpc_client_streaming_service_client.bal ================================================ import ballerina/io; public function main() returns error? { // Creates a gRPC client to interact with the remote server. HelloWorldClient ep = check new ("http://localhost:9090"); // Executes the client-streaming RPC call and receives the streaming client. LotsOfGreetingsStreamingClient streamingClient = check ep->lotsOfGreetings(); // Sends multiple messages to the server. string[] requests = ["Hi Sam", "Hey Sam", "GM Sam"]; foreach var greet in requests { check streamingClient->sendString(greet); } // Once all the messages are sent, the server notifies the caller with a `complete` message. check streamingClient->complete(); // Receives the server response. string? response = check streamingClient->receiveString(); io:println(response); } ================================================ FILE: examples/grpc-client-client-streaming/grpc_client_streaming_service_client.out ================================================ $ bal run client Ack ================================================ FILE: examples/grpc-client-headers/grpc_simple.proto ================================================ // This is the service definition of the simple scenario. syntax = "proto3"; import "google/protobuf/wrappers.proto"; service HelloWorld { rpc hello (google.protobuf.StringValue) returns (google.protobuf.StringValue); } ================================================ FILE: examples/grpc-client-headers/grpc_simple_with_headers.md ================================================ # gRPC client - Send/Receive headers The `grpc:Client` allows sending headers and receiving headers to/from a gRPC server. The gRPC - Protobuf CLI tool generates a `Context` record for each Protobuf message type, which contains the Protobuf message and the header map. The header map supports `string`and `string[]` types. A Context record value is created with the required headers and sent using the `context` method of the client (`helloContext()`). The `Contex type of the required record is provided as the target type of the response to receive headers. The `getHeader` and `getHeaders` methods are also available to manipulate the header values. ::: code grpc_simple_with_headers_service_client.bal ::: Setting up the client is the same as setting up the simple RPC client with input and output parameter change. For information on implementing the client, see [gRPC client - Simple RPC](/learn/by-example/grpc-client-simple/). ## Prerequisites - Run the gRPC service given in the [gRPC service - Send/Receive headers](/learn/by-example/grpc-service-headers/) example. Run the client by executing the command below. ::: out grpc_simple_with_headers_service_client.out ::: ## Related links - [`grpc` module - API documentation](https://lib.ballerina.io/ballerina/grpc/latest) - [`grpc` module - Specification](/spec/grpc/) ================================================ FILE: examples/grpc-client-headers/grpc_simple_with_headers.metatags ================================================ description: This Ballerina by Example has a gRPC simple client sends and receives messages and headers through a remote call. keywords: ballerina, ballerina by example, bbe, grpc, unary, simple, client, headers, metadata ================================================ FILE: examples/grpc-client-headers/grpc_simple_with_headers_service_client.bal ================================================ import ballerina/grpc; import ballerina/io; import ballerina/protobuf.types.wrappers; public function main() returns error? { // Creates a gRPC client to interact with the remote server. HelloWorldClient ep = check new ("http://localhost:9090"); // Creates the request message with the header value. wrappers:ContextString requestMessage = {content: "WSO2", headers: {client_header_key: "0987654321"}}; // Executes a simple remote call. wrappers:ContextString result = check ep->helloContext(requestMessage); // Prints the received result. io:println(result.content); // Reads the header value in the response message and prints it. io:println(check grpc:getHeader(result.headers, "server_header_key")); } ================================================ FILE: examples/grpc-client-headers/grpc_simple_with_headers_service_client.out ================================================ $ bal run client Hello WSO2 Response Header value ================================================ FILE: examples/grpc-client-mutual-ssl/grpc_client_mutual_ssl.bal ================================================ import ballerina/io; public function main() returns error? { // The gRPC client can be configured to initiate new connections that are secured via mutual SSL. HelloWorldClient securedEP = check new("https://localhost:9090", secureSocket = { key: { certFile: "../resource/path/to/public.crt", keyFile: "../resource/path/to/private.key" }, cert: "../resource/path/to/public.crt" } ); string result = check securedEP->hello("WSO2"); io:println(result); } ================================================ FILE: examples/grpc-client-mutual-ssl/grpc_client_mutual_ssl.md ================================================ # gRPC client - Mutual SSL The `grpc:Client` allows opening up a connection secured with mutual SSL (mTLS), which is a certificate-based authentication process in which two parties (the client and server) authenticate each other by verifying the digital certificates. It ensures that both parties are assured of each other's identity. The `grpc:Client` secured with mutual SSL is created by providing the `secureSocket` configurations, which require the client's public certificate as the `certFile`, the client's private key as the `keyFile`, and the server's certificate as the `cert`. Use this to interact with mTLS-encrypted gRPC servers. ::: code grpc_client_mutual_ssl.bal ::: Setting up the client is the same as setting up the simple RPC client with additional configurations. For information on implementing the client, see [gRPC client - Simple RPC](/learn/by-example/grpc-client-simple/). ## Prerequisites - Run the gRPC service given in the [gRPC service - Mutual SSL](/learn/by-example/grpc-service-mutual-ssl/) example. Run the client by executing the command below. ::: out grpc_client_mutual_ssl.out ::: ## Related links - [`grpc:ClientSecureSocket` record - API documentation](https://lib.ballerina.io/ballerina/grpc/latest#ClientSecureSocket) - [gRPC client mutual SSL - Specification](/spec/grpc/#52-ssltls-and-mutual-ssl) ================================================ FILE: examples/grpc-client-mutual-ssl/grpc_client_mutual_ssl.metatags ================================================ description: BBE on how to secure gRPC client with mutual SSL. keywords: ballerina, ballerina by example, bbe, grpc, mutual ssl, ssl protocols, ciphers ================================================ FILE: examples/grpc-client-mutual-ssl/grpc_client_mutual_ssl.out ================================================ $ bal run client Hello WSO2 ================================================ FILE: examples/grpc-client-mutual-ssl/grpc_simple.proto ================================================ // This is the service definition of the simple scenario. syntax = "proto3"; import "google/protobuf/wrappers.proto"; service HelloWorld { rpc hello (google.protobuf.StringValue) returns (google.protobuf.StringValue); } ================================================ FILE: examples/grpc-client-oauth2-client-credentials-grant-type/grpc_client_oauth2_client_credentials_grant_type.bal ================================================ import ballerina/io; public function main() returns error? { // Defines the gRPC client to call the OAuth2-secured APIs. HelloWorldClient securedEP = check new("https://localhost:9090", auth = { tokenUrl: "https://localhost:9445/oauth2/token", clientId: "FlfJYKBD2c925h4lkycqNZlC2l4a", clientSecret: "PJz0UhTJMrHOo68QQNpvnqAY_3Aa", scopes: "admin", clientConfig: { secureSocket: { cert: "../resource/path/to/public.crt" } } }, secureSocket = { cert: "../resource/path/to/public.crt" } ); string result = check securedEP->hello("WSO2"); io:println(result); } ================================================ FILE: examples/grpc-client-oauth2-client-credentials-grant-type/grpc_client_oauth2_client_credentials_grant_type.md ================================================ # gRPC client - OAuth2 client credentials grant type The `grpc:Client` can connect to a service that is secured with the OAuth2 client credentials grant type by enriching client metadata with the `Authorization: Bearer ` header. The required configurations for this grant type can be specified in the `auth` field of the client configuration. ::: code grpc_client_oauth2_client_credentials_grant_type.bal ::: Setting up the client is the same as setting up the simple RPC client with additional configurations. For information on implementing the client, see [gRPC client - Simple RPC](/learn/by-example/grpc-client-simple/). ## Prerequisites - Run the gRPC service given in the [gRPC service - OAuth2](/learn/by-example/grpc-service-oauth2/) example. Run the client by executing the command below. ::: out grpc_client_oauth2_client_credentials_grant_type.out ::: ## Related links - [`grpc:OAuth2ClientCredentialsGrantConfig` record - API documentation](https://lib.ballerina.io/ballerina/grpc/latest#OAuth2ClientCredentialsGrantConfig) - [gRPC client OAuth2 authentication and authorization - Specification](/spec/grpc/#5118-client---oauth2) - [`oauth2` module - API documentation](https://lib.ballerina.io/ballerina/oauth2/latest/) ================================================ FILE: examples/grpc-client-oauth2-client-credentials-grant-type/grpc_client_oauth2_client_credentials_grant_type.metatags ================================================ description: BBE on how to secure gRPC client with OAuth2 client credentials grant type in Ballerina. keywords: ballerina, ballerina by example, bbe, grpc, auth, oauth2, client credentials grant type ================================================ FILE: examples/grpc-client-oauth2-client-credentials-grant-type/grpc_client_oauth2_client_credentials_grant_type.out ================================================ $ bal run client Hello WSO2 ================================================ FILE: examples/grpc-client-oauth2-client-credentials-grant-type/grpc_simple.proto ================================================ // This is the service definition of the simple scenario. syntax = "proto3"; import "google/protobuf/wrappers.proto"; service HelloWorld { rpc hello (google.protobuf.StringValue) returns (google.protobuf.StringValue); } ================================================ FILE: examples/grpc-client-oauth2-jwt-bearer-grant-type/grpc_client_oauth2_jwt_bearer_grant_type.bal ================================================ import ballerina/io; public function main() returns error? { // Defines the gRPC client to call the OAuth2-secured APIs. HelloWorldClient securedEP = check new("https://localhost:9090", auth = { tokenUrl: "https://localhost:9445/oauth2/token", assertion: "eyJhbGciOiJFUzI1NiIsImtpZCI6Ij[...omitted for brevity...]", clientId: "FlfJYKBD2c925h4lkycqNZlC2l4a", clientSecret: "PJz0UhTJMrHOo68QQNpvnqAY_3Aa", scopes: "admin", clientConfig: { secureSocket: { cert: "../resource/path/to/public.crt" } } }, secureSocket = { cert: "../resource/path/to/public.crt" } ); string result = check securedEP->hello("WSO2"); io:println(result); } ================================================ FILE: examples/grpc-client-oauth2-jwt-bearer-grant-type/grpc_client_oauth2_jwt_bearer_grant_type.md ================================================ # gRPC client - OAuth2 JWT bearer grant type The `grpc:Client` can connect to a service that is secured with the OAuth2 JWT bearer grant type by enriching the client metadata with the `Authorization: Bearer ` header. The required configurations for this grant type can be specified in the `auth` field of the client configuration. ::: code grpc_client_oauth2_jwt_bearer_grant_type.bal ::: Setting up the client is the same as setting up the simple RPC client with additional configurations. For information on implementing the client, see [gRPC client - Simple RPC](/learn/by-example/grpc-client-simple/). ## Prerequisites - Run the gRPC service given in the [gRPC service - OAuth2](/learn/by-example/grpc-service-oauth2/) example. Run the client by executing the command below. ::: out grpc_client_oauth2_jwt_bearer_grant_type.out ::: ## Related links - [`grpc:OAuth2JwtBearerGrantConfig` record - API documentation](https://lib.ballerina.io/ballerina/grpc/latest#OAuth2JwtBearerGrantConfig) - [gRPC client OAuth2 authentication and authorization - Specification](/spec/grpc/#5118-client---oauth2) - [`oauth2` module - API documentation](https://lib.ballerina.io/ballerina/oauth2/latest/) ================================================ FILE: examples/grpc-client-oauth2-jwt-bearer-grant-type/grpc_client_oauth2_jwt_bearer_grant_type.metatags ================================================ description: BBE on how to secure gRPC client with OAuth2 JWT bearer grant type in Ballerina. keywords: ballerina, ballerina by example, bbe, grpc, auth, oauth2, jwt bearer grant type ================================================ FILE: examples/grpc-client-oauth2-jwt-bearer-grant-type/grpc_client_oauth2_jwt_bearer_grant_type.out ================================================ $ bal run client Hello WSO2 ================================================ FILE: examples/grpc-client-oauth2-jwt-bearer-grant-type/grpc_simple.proto ================================================ // This is the service definition of the simple scenario. syntax = "proto3"; import "google/protobuf/wrappers.proto"; service HelloWorld { rpc hello (google.protobuf.StringValue) returns (google.protobuf.StringValue); } ================================================ FILE: examples/grpc-client-oauth2-password-grant-type/grpc_client_oauth2_password_grant_type.bal ================================================ import ballerina/io; import ballerina/oauth2; public function main() returns error? { // Defines the gRPC client to call the OAuth2-secured APIs. HelloWorldClient securedEP = check new("https://localhost:9090", auth = { tokenUrl: "https://localhost:9445/oauth2/token", username: "admin", password: "admin", clientId: "FlfJYKBD2c925h4lkycqNZlC2l4a", clientSecret: "PJz0UhTJMrHOo68QQNpvnqAY_3Aa", scopes: "admin", refreshConfig: oauth2:INFER_REFRESH_CONFIG, clientConfig: { secureSocket: { cert: "../resource/path/to/public.crt" } } }, secureSocket = { cert: "../resource/path/to/public.crt" } ); string result = check securedEP->hello("WSO2"); io:println(result); } ================================================ FILE: examples/grpc-client-oauth2-password-grant-type/grpc_client_oauth2_password_grant_type.md ================================================ # gRPC client - OAuth2 password grant type The `grpc:Client` can connect to a service that is secured with the OAuth2 password grant type by enriching the client metadata with the `Authorization: Bearer ` header. The required configurations for this grant type can be specified in the `auth` field of the client configuration. Use this grant type when you need to exchange the user's credentials for an access token. ::: code grpc_client_oauth2_password_grant_type.bal ::: Setting up the client is the same as setting up the simple RPC client with additional configurations. For information on implementing the client, see [gRPC client - Simple RPC](/learn/by-example/grpc-client-simple/). ## Prerequisites - Run the gRPC service given in the [gRPC service - OAuth2](/learn/by-example/grpc-service-oauth2/) example. Run the client by executing the command below. ::: out grpc_client_oauth2_password_grant_type.out ::: ## Related links - [`grpc:OAuth2PasswordGrantConfig` record - API documentation](https://lib.ballerina.io/ballerina/grpc/latest#OAuth2PasswordGrantConfig) - [gRPC client OAuth2 authentication and authorization - Specification](/spec/grpc/#5118-client---oauth2) - [`oauth2` module - API documentation](https://lib.ballerina.io/ballerina/oauth2/latest/) ================================================ FILE: examples/grpc-client-oauth2-password-grant-type/grpc_client_oauth2_password_grant_type.metatags ================================================ description: BBE on how to secure gRPC client with OAuth2 password grant type in Ballerina. keywords: ballerina, ballerina by example, bbe, grpc, auth, oauth2, password grant type ================================================ FILE: examples/grpc-client-oauth2-password-grant-type/grpc_client_oauth2_password_grant_type.out ================================================ $ bal run client Hello WSO2 ================================================ FILE: examples/grpc-client-oauth2-password-grant-type/grpc_simple.proto ================================================ // This is the service definition of the simple scenario. syntax = "proto3"; import "google/protobuf/wrappers.proto"; service HelloWorld { rpc hello (google.protobuf.StringValue) returns (google.protobuf.StringValue); } ================================================ FILE: examples/grpc-client-oauth2-refresh-token-grant-type/grpc_client_oauth2_refresh_token_grant_type.bal ================================================ import ballerina/io; public function main() returns error? { // Defines the gRPC client to call the OAuth2-secured APIs. HelloWorldClient securedEP = check new("https://localhost:9090", auth = { refreshUrl: "https://localhost:9445/oauth2/token", refreshToken: "24f19603-8565-4b5f-a036-88a945e1f272", clientId: "FlfJYKBD2c925h4lkycqNZlC2l4a", clientSecret: "PJz0UhTJMrHOo68QQNpvnqAY_3Aa", scopes: "admin", clientConfig: { secureSocket: { cert: "../resource/path/to/public.crt" } } }, secureSocket = { cert: "../resource/path/to/public.crt" } ); string result = check securedEP->hello("WSO2"); io:println(result); } ================================================ FILE: examples/grpc-client-oauth2-refresh-token-grant-type/grpc_client_oauth2_refresh_token_grant_type.md ================================================ # gRPC client - OAuth2 refresh token grant type The `grpc:Client` can connect to a service that is secured with the OAuth2 refresh token grant type by enriching the client metadata with the `Authorization: Bearer ` header. The required configurations for this grant type can be specified in the `auth` field of the client configuration. Use this to retrieve an access token automatically when it is expired. ::: code grpc_client_oauth2_refresh_token_grant_type.bal ::: Setting up the client is the same as setting up the simple RPC client with additional configurations. For information on implementing the client, see [gRPC client - Simple RPC](/learn/by-example/grpc-client-simple/). ## Prerequisites - Run the gRPC service given in the [gRPC service - OAuth2](/learn/by-example/grpc-service-oauth2/) example. Run the client by executing the command below. ::: out grpc_client_oauth2_refresh_token_grant_type.out ::: ## Related links - [`grpc:OAuth2RefreshTokenGrantConfig` record - API documentation](https://lib.ballerina.io/ballerina/grpc/latest#OAuth2RefreshTokenGrantConfig) - [gRPC client OAuth2 authentication and authorization - Specification](/spec/grpc/#5118-client---oauth2) - [`oauth2` module - API documentation](https://lib.ballerina.io/ballerina/oauth2/latest/) ================================================ FILE: examples/grpc-client-oauth2-refresh-token-grant-type/grpc_client_oauth2_refresh_token_grant_type.metatags ================================================ description: BBE on how to secure gRPC client with OAuth2 refresh token grant type in Ballerina. keywords: ballerina, ballerina by example, bbe, grpc, auth, oauth2, refresh token grant type ================================================ FILE: examples/grpc-client-oauth2-refresh-token-grant-type/grpc_client_oauth2_refresh_token_grant_type.out ================================================ $ bal run client Hello WSO2 ================================================ FILE: examples/grpc-client-oauth2-refresh-token-grant-type/grpc_simple.proto ================================================ // This is the service definition of the simple scenario. syntax = "proto3"; import "google/protobuf/wrappers.proto"; service HelloWorld { rpc hello (google.protobuf.StringValue) returns (google.protobuf.StringValue); } ================================================ FILE: examples/grpc-client-self-signed-jwt-auth/grpc_client_self_signed_jwt_auth.bal ================================================ import ballerina/io; public function main() returns error? { // Defines the gRPC client to call the JWT-secured APIs. HelloWorldClient securedEP = check new("https://localhost:9090", auth = { username: "ballerina", issuer: "wso2", audience: ["ballerina", "ballerina.org", "ballerina.io"], keyId: "5a0b754-895f-4279-8843-b745e11a57e9", jwtId: "JlbmMiOiJBMTI4Q0JDLUhTMjU2In", customClaims: { "scp": "admin" }, expTime: 3600, signatureConfig: { config: { keyFile: "../resource/path/to/private.key" } } }, secureSocket = { cert: "../resource/path/to/public.crt" } ); string result = check securedEP->hello("WSO2"); io:println(result); } ================================================ FILE: examples/grpc-client-self-signed-jwt-auth/grpc_client_self_signed_jwt_auth.md ================================================ # gRPC client - Self signed JWT authentication The `grpc:Client` can connect to a service that is secured with self-signed JWT by enriching the client metadata with the `Authorization: Bearer ` header by passing the `grpc:JwtIssuerConfig` to the auth configuration of the client. A self-signed JWT is issued before the request is sent. ::: code grpc_client_self_signed_jwt_auth.bal ::: Setting up the client is the same as setting up the simple RPC client with additional configurations. For information on implementing the client, see [gRPC client - Simple RPC](/learn/by-example/grpc-client-simple/). ## Prerequisites - Run the gRPC service given in the [gRPC service - JWT authentication](/learn/by-example/grpc-service-jwt-auth/) example. Run the client by executing the command below. ::: out grpc_client_self_signed_jwt_auth.out ::: ## Related links - [`grpc:JwtIssuerConfig` record - API documentation](https://lib.ballerina.io/ballerina/grpc/latest#JwtIssuerConfig) - [gRPC client self signed JWT authentication - Specification](/spec/grpc/#5117-client---self-signed-jwt-auth) - [`oauth2` module - API documentation](https://lib.ballerina.io/ballerina/oauth2/latest/) ================================================ FILE: examples/grpc-client-self-signed-jwt-auth/grpc_client_self_signed_jwt_auth.metatags ================================================ description: BBE on how to secure gRPC client with self-signed JWT Auth in Ballerina. keywords: ballerina, ballerina by example, bbe, grpc, auth, jwt auth ================================================ FILE: examples/grpc-client-self-signed-jwt-auth/grpc_client_self_signed_jwt_auth.out ================================================ $ bal run client Hello WSO2 ================================================ FILE: examples/grpc-client-self-signed-jwt-auth/grpc_simple.proto ================================================ // This is the service definition of the simple scenario. syntax = "proto3"; import "google/protobuf/wrappers.proto"; service HelloWorld { rpc hello (google.protobuf.StringValue) returns (google.protobuf.StringValue); } ================================================ FILE: examples/grpc-client-server-streaming/grpc_server_streaming.md ================================================ # gRPC client - Server-side streaming RPC A `grpc:Client` is created by providing the endpoint URL of a gRPC server. In the server streaming scenario, once connected, the client sends a request message to the remote service and gets a message stream as the response, which contains multiple messages. Use this to send a single request message and get multiple response messages back. ## Generate the service definition 1. Create a new Protocol Buffers definition file `grpc_server_streaming.proto` and add service definition below. ::: code grpc_server_streaming.proto ::: 2. Run the command below in the Ballerina tools distribution for stub generation. ::: out grpc_server_streaming.out ::: Once you run the command, the `grpc_server_streaming_pb.bal` file gets generated inside the stubs directory. ## Prerequisites - Run the gRPC service given in the [gRPC service - Server-side streaming RPC](/learn/by-example/grpc-service-server-streaming/) example. ## Implement and run the client 1. Create a Ballerina package (e.g., `client`). Delete the `main.bal` file created by default as it is not required for this example. 2. Copy the generated `grpc_server_streaming_pb.bal` file from the `stubs` directory to the `client` package. 3. Create a new `grpc_server_streaming_client.bal` file inside the `client` package and add the client implementation below. ::: code grpc_server_streaming_service_client.bal ::: 4. Run the client by executing the command below. ::: out grpc_server_streaming_service_client.out ::: ## Related links - [`grpc` module - API documentation](https://lib.ballerina.io/ballerina/grpc/latest) - [gRPC client server-side streaming - Specification](/spec/grpc/#42-server-streaming-rpc) - [gRPC tool](/learn/grpc-tool/) ================================================ FILE: examples/grpc-client-server-streaming/grpc_server_streaming.metatags ================================================ description: This Ballerina by Example has a gRPC client for server streaming RPC. keywords: ballerina, ballerina by example, bbe, grpc, server streaming ================================================ FILE: examples/grpc-client-server-streaming/grpc_server_streaming.out ================================================ $ bal grpc --input grpc_server_streaming.proto --output stubs ================================================ FILE: examples/grpc-client-server-streaming/grpc_server_streaming.proto ================================================ // This is the service definition for the server streaming scenario. syntax = "proto3"; import "google/protobuf/wrappers.proto"; service HelloWorld { rpc lotsOfReplies (google.protobuf.StringValue) returns (stream google.protobuf.StringValue); } ================================================ FILE: examples/grpc-client-server-streaming/grpc_server_streaming_service_client.bal ================================================ import ballerina/grpc; import ballerina/io; public function main() returns error? { // Creates a gRPC client to interact with the remote server. HelloWorldClient ep = check new ("http://localhost:9090"); // Executes the streaming RPC call and gets the response as a stream. stream result = check ep->lotsOfReplies("WSO2"); // Iterates through the stream and prints the content. check result.forEach(function(string str) { io:println(str); }); } ================================================ FILE: examples/grpc-client-server-streaming/grpc_server_streaming_service_client.out ================================================ $ bal run client Hi WSO2 Hey WSO2 GM WSO2 ================================================ FILE: examples/grpc-client-set-deadline/grpc_client_set_deadline.bal ================================================ import ballerina/grpc; import ballerina/io; import ballerina/protobuf.types.wrappers; import ballerina/time; public function main() returns error? { HelloWorldClient ep = check new ("http://localhost:9090"); // Get a deadline time by adding 5 seconds to the current time. time:Utc current = time:utcNow(); time:Utc deadline = time:utcAddSeconds(current, 5); // Set the deadline time as a header. map headers = grpc:setDeadline(deadline); wrappers:ContextString|grpc:Error response = ep->helloContext({content: "WSO2", headers: headers}); if response is grpc:Error { io:println("An error has occured : " + response.message()); } else { io:println(response.content); } } ================================================ FILE: examples/grpc-client-set-deadline/grpc_client_set_deadline.md ================================================ # gRPC client - Set deadline The `grpc:Client` allows setting deadlines to specify how long they are willing to wait for an RPC to complete before the RPC is terminated with an error. The deadline is set as a header using the `setDeadline` method. Use this to set an upper limit on how long a call can run. ::: code grpc_client_set_deadline.bal ::: Setting up the client is the same as setting up the simple RPC client with additional configurations. For information on implementing the client, see [gRPC client - Simple RPC](/learn/by-example/grpc-client-simple/). ## Prerequisites - Run the gRPC service given in the [gRPC service - Check deadline](/learn/by-example/grpc-service-check-deadline/) example. Run the client by executing the command below. ::: out grpc_client_set_deadline.out ::: ## Related links - [`grpc:setDeadline` function - API documentation](https://lib.ballerina.io/ballerina/grpc/latest#setDeadline) - [gRPC client set deadline - Specification](/spec/grpc/#61-grpc-deadline) ================================================ FILE: examples/grpc-client-set-deadline/grpc_client_set_deadline.metatags ================================================ description: This Ballerina by Example has a gRPC client with a configured deadline. keywords: ballerina, ballerina by example, bbe, grpc, unary, client, deadline ================================================ FILE: examples/grpc-client-set-deadline/grpc_client_set_deadline.out ================================================ $ bal run client An error has occured : Exceeded the configured deadline ================================================ FILE: examples/grpc-client-set-deadline/grpc_simple.proto ================================================ // This is the service definition of the simple scenario. syntax = "proto3"; import "google/protobuf/wrappers.proto"; service HelloWorld { rpc hello (google.protobuf.StringValue) returns (google.protobuf.StringValue); } ================================================ FILE: examples/grpc-client-simple/grpc_client_simple.bal ================================================ import ballerina/io; public function main() returns error? { // Creates a gRPC client to interact with the remote server. HelloWorldClient ep = check new ("http://localhost:9090"); // Executes a simple remote call. string result = check ep->hello("WSO2"); // Prints the received result. io:println(result); } ================================================ FILE: examples/grpc-client-simple/grpc_client_simple.md ================================================ # gRPC client - Simple RPC A `grpc:Client` is created by providing the endpoint URL of a gRPC server. In the simple RPC scenario, once connected, the client sends a request message to the remote service and waits for the response message. Use this to send a single request message and get a single response message back. ## Generate the service definition 1. Create a new Protocol Buffers definition file named `grpc_simple.proto` and add the service definition below. ::: code grpc_client_simple.proto ::: 2. Run the command below from the Ballerina tools distribution for stub generation. ::: out grpc_simple.out ::: Once you run the command, the `grpc_simple_pb.bal` file gets generated inside the `stubs` directory. ## Prerequisites - Run the gRPC service given in the [gRPC service - Simple RPC](/learn/by-example/grpc-service-simple/) example. ## Implement and run the client 1. Create a Ballerina package (e.g., `client`). Delete the `main.bal` file created by default as it is not required for this example. 2. Copy the generated `grpc_simple_pb.bal` file from the `stubs` directory to the `client` package. 3. Create a new `grpc_simple_client.bal` file inside the `client` package and add the client implementation below. ::: code grpc_client_simple.bal ::: 4. Run the client by executing the command below. ::: out grpc_client_simple.out ::: ## Related links - [`grpc` module - API documentation](https://lib.ballerina.io/ballerina/grpc/latest) - [gRPC client simple RPC - Specification](/spec/grpc/#41-simple-rpc) - [gRPC tool](/learn/grpc-tool/) ================================================ FILE: examples/grpc-client-simple/grpc_client_simple.metatags ================================================ description: This Ballerina by Example has a gRPC client for Simple RPC. keywords: ballerina, ballerina by example, bbe, grpc, unary, simple, client ================================================ FILE: examples/grpc-client-simple/grpc_client_simple.out ================================================ $ bal run client Hello WSO2 ================================================ FILE: examples/grpc-client-simple/grpc_client_simple.proto ================================================ syntax = "proto3"; import "google/protobuf/wrappers.proto"; service HelloWorld { rpc hello (google.protobuf.StringValue) returns (google.protobuf.StringValue); } ================================================ FILE: examples/grpc-client-simple/grpc_simple.out ================================================ $ bal grpc --input grpc_simple.proto --output stubs ================================================ FILE: examples/grpc-client-ssl-tls/grpc_client_ssl_tls.bal ================================================ import ballerina/io; public function main() returns error? { HelloWorldClient securedEP = check new("https://localhost:9090", secureSocket = { cert: "../resource/path/to/public.crt" } ); string result = check securedEP->hello("WSO2"); io:println(result); } ================================================ FILE: examples/grpc-client-ssl-tls/grpc_client_ssl_tls.md ================================================ # gRPC client - SSL/TLS The `grpc:Client` can be configured to communicate through HTTPS by providing a certificate file. The certificate can be provided through the `secureSocket` field of the client configuration. Use this to secure the communication between the client and the server. ::: code grpc_client_ssl_tls.bal ::: Setting up the client is the same as setting up the simple RPC client with additional configurations. For information on implementing the client, see [gRPC client - Simple RPC](/learn/by-example/grpc-client-simple/). ## Prerequisites - Run the gRPC service given in the [gRPC service - SSL/TLS](/learn/by-example/grpc-service-ssl-tls/) example. Run the client by executing the command below. ::: out grpc_client_ssl_tls.out ::: ## Related links - [`grpc:ClientSecureSocket` record - API documentation](https://lib.ballerina.io/ballerina/grpc/latest#ClientSecureSocket) - [gRPC client SSL/TLS - Specification](/spec/grpc/#52-ssltls-and-mutual-ssl) ================================================ FILE: examples/grpc-client-ssl-tls/grpc_client_ssl_tls.metatags ================================================ description: BBE on how to secure gRPC client with SSL. keywords: ballerina, ballerina by example, bbe, grpc, ssl, tls ================================================ FILE: examples/grpc-client-ssl-tls/grpc_client_ssl_tls.out ================================================ $ bal run client Hello WSO2 ================================================ FILE: examples/grpc-client-ssl-tls/grpc_simple.proto ================================================ // This is the service definition of the simple scenario. syntax = "proto3"; import "google/protobuf/wrappers.proto"; service HelloWorld { rpc hello (google.protobuf.StringValue) returns (google.protobuf.StringValue); } ================================================ FILE: examples/grpc-server-reflection/grpc_server_reflection.bal ================================================ import ballerina/grpc; @grpc:Descriptor { value: GRPC_SIMPLE_DESC } service "HelloWorld" on new grpc:Listener(9090, {reflectionEnabled: true}) { remote function hello(string request) returns string { return "Hello " + request; } } ================================================ FILE: examples/grpc-server-reflection/grpc_server_reflection.md ================================================ # gRPC service - Server reflection The `grpc:Listener` provides the server reflection capability, which allows dynamic clients such as command-line tools for debugging to discover the protocol used by a gRPC server at run time. Server reflection is enabled by providing the `reflectionEnabled` configuration in the `grpc:ListenerConfiguration`. ::: code grpc_server_reflection.bal ::: Setting up the service is the same as setting up the simple RPC service with additional configurations. For information on implementing the service, see [gRPC service - Simple RPC](/learn/by-example/grpc-service-simple/). Run the service by executing the command below. ::: out grpc_server_reflection.out ::: After running the service, you can use a tool like [`gRPCurl`](https://github.com/fullstorydev/grpcurl), [`Postman`](https://www.postman.com/), [`evans CLI`](https://github.com/ktr0731/evans) to inspect the service. ::: out grpc_server_reflection_grpcurl.out ::: ## Related links - [`grpc:ListenerConfiguration` record - API documentation](https://lib.ballerina.io/ballerina/grpc/latest#ListenerConfiguration) - [gRPC service server reflection - Specification](/spec/grpc/#7-grpc-server-reflection) ================================================ FILE: examples/grpc-server-reflection/grpc_server_reflection.metatags ================================================ description: This Ballerina by Example show how to enabled server reflection for a gRPC service. keywords: ballerina, ballerina by example, bbe, grpc, unary, simple, service, reflection ================================================ FILE: examples/grpc-server-reflection/grpc_server_reflection.out ================================================ $ bal run service ================================================ FILE: examples/grpc-server-reflection/grpc_server_reflection_grpcurl.out ================================================ $ ./grpcurl -use-reflection -plaintext localhost:9090 describe HelloWorld is a service: service HelloWorld { rpc hello ( .google.protobuf.StringValue ) returns ( .google.protobuf.StringValue ); } grpc.reflection.v1alpha.ServerReflection is a service: service ServerReflection { rpc ServerReflectionInfo ( stream .grpc.reflection.v1alpha.ServerReflectionRequest ) returns ( stream .grpc.reflection.v1alpha.ServerReflectionResponse ); } ================================================ FILE: examples/grpc-server-reflection/grpc_simple.proto ================================================ // This is the service definition of the simple scenario. syntax = "proto3"; import "google/protobuf/wrappers.proto"; service HelloWorld { rpc hello (google.protobuf.StringValue) returns (google.protobuf.StringValue); } ================================================ FILE: examples/grpc-service-basic-auth-file-user-store/grpc_service_basic_auth_file_user_store.bal ================================================ import ballerina/grpc; listener grpc:Listener securedEP = new (9090, secureSocket = { key: { certFile: "../resource/path/to/public.crt", keyFile: "../resource/path/to/private.key" } } ); // Basic authentication with the file user store can be enabled by setting // the `grpc:FileUserStoreConfig` configuration. // Authorization is based on scopes, which can be specified in the `scopes` field. @grpc:ServiceConfig { auth: [ { fileUserStoreConfig: {}, scopes: ["admin"] } ] } @grpc:Descriptor { value: GRPC_SIMPLE_DESC } service "HelloWorld" on securedEP { remote function hello(string request) returns string { return "Hello " + request; } } ================================================ FILE: examples/grpc-service-basic-auth-file-user-store/grpc_service_basic_auth_file_user_store.md ================================================ # gRPC service - Basic authentication file user store The `grpc:Service` can be secured with basic authentication and additionally, scopes can be added to enforce authorization. It validates the basic authentication token sent in the `Authorization` metadata against the provided configurations provided in the `Config.toml` file. The file stores the usernames and passwords for the authentication and the scopes for the authorization. To engage authentication, set the default values for the `fileUserStoreConfig` field and add the `Config.toml` file next to the service file. To engage authorization, set the scopes to the `scopes` field. Both configurations must be given as part of the service configuration. A `grpc:UnauthenticatedError` is sent to the client when the authentication fails, and a `grpc:PermissionDeniedError` is sent to the client when the authorization fails. Use this to authenticate and authorize requests based on user stores. ::: code grpc_service_basic_auth_file_user_store.bal ::: ## Prerequisites - Populate the `Config.toml` file correctly with the user information as shown below. ::: code Config.toml ::: Setting up the service is the same as setting up the simple RPC service with additional configurations. For information on implementing the service, see [gRPC service - Simple RPC](/learn/by-example/grpc-service-simple/). Run the service by executing the command below. ::: out grpc_service_basic_auth_file_user_store.server.out ::: >**Tip:** You can invoke the above service via the [gRPC client - Basic authentication](/learn/by-example/grpc-client-basic-auth). ## Related links - [`grpc:FileUserStoreConfig` record - API documentation](https://lib.ballerina.io/ballerina/grpc/latest#FileUserStoreConfig) - [gRPC service basic authentication file user store - Specification](/spec/grpc/#5111-service---basic-auth---file-user-store) - [`auth` module - API documentation](https://lib.ballerina.io/ballerina/auth/latest/) ================================================ FILE: examples/grpc-service-basic-auth-file-user-store/grpc_service_basic_auth_file_user_store.metatags ================================================ description: BBE on how to secure gRPC service with Basic Auth in Ballerina. keywords: ballerina, ballerina by example, bbe, grpc, auth, basic auth ================================================ FILE: examples/grpc-service-basic-auth-file-user-store/grpc_service_basic_auth_file_user_store.server.out ================================================ $ bal run service ================================================ FILE: examples/grpc-service-basic-auth-file-user-store/grpc_simple.proto ================================================ // This is the service definition of the simple scenario. syntax = "proto3"; import "google/protobuf/wrappers.proto"; service HelloWorld { rpc hello (google.protobuf.StringValue) returns (google.protobuf.StringValue); } ================================================ FILE: examples/grpc-service-basic-auth-ldap-user-store/grpc_service_basic_auth_ldap_user_store.bal ================================================ import ballerina/grpc; listener grpc:Listener securedEP = new (9090, secureSocket = { key: { certFile: "../resource/path/to/public.crt", keyFile: "../resource/path/to/private.key" } } ); // Basic authentication with the LDAP user store can be enabled by setting // the `grpc:LdapUserStoreConfig` configuration. // Authorization is based on scopes, which can be specified in the `scopes` field. @grpc:ServiceConfig { auth: [ { ldapUserStoreConfig: { domainName: "avix.lk", connectionUrl: "ldap://localhost:389", connectionName: "cn=admin,dc=avix,dc=lk", connectionPassword: "avix123", userSearchBase: "ou=Users,dc=avix,dc=lk", userEntryObjectClass: "inetOrgPerson", userNameAttribute: "uid", userNameSearchFilter: "(&(objectClass=inetOrgPerson)(uid=?))", userNameListFilter: "(objectClass=inetOrgPerson)", groupSearchBase: ["ou=Groups,dc=avix,dc=lk"], groupEntryObjectClass: "groupOfNames", groupNameAttribute: "cn", groupNameSearchFilter: "(&(objectClass=groupOfNames)(cn=?))", groupNameListFilter: "(objectClass=groupOfNames)", membershipAttribute: "member", userRolesCacheEnabled: true, connectionPoolingEnabled: false, connectionTimeout: 5, readTimeout: 60 }, scopes: ["admin"] } ] } @grpc:Descriptor { value: GRPC_SIMPLE_DESC } service "HelloWorld" on securedEP { remote function hello(string request) returns string { return "Hello " + request; } } ================================================ FILE: examples/grpc-service-basic-auth-ldap-user-store/grpc_service_basic_auth_ldap_user_store.md ================================================ # gRPC service - Basic authentication LDAP user store The `grpc:Service` can be secured with basic authentication and additionally, scopes can be added to enforce authorization. It validates the basic authentication token sent in the `Authorization` metadata with the LDAP server. This server stores the usernames and passwords for the authentication and the scopes for the authorization. To engage authentication, set the LDAP-related configurations to the `ldapUserStoreConfig` field. To engage authorization, set the scopes to the `scopes` field. Both configurations must be given as part of the service configuration. A `grpc:UnauthenticatedError` is sent to the client when the authentication fails, and a `grpc:PermissionDeniedError` is sent to the client when the authorization fails. Use this to authenticate and authorize requests based on LDAP user stores. ::: code grpc_service_basic_auth_ldap_user_store.bal ::: Setting up the service is the same as setting up the simple RPC service with additional configurations. For information on implementing the service, see [gRPC service - Simple RPC](/learn/by-example/grpc-service-simple/). ## Prerequisites - Run the LDAP server. Run the service by executing the command below. ::: out grpc_service_basic_auth_ldap_user_store.server.out ::: >**Tip:** You can invoke the above service via the [gRPC client - Basic authentication](/learn/by-example/grpc-client-basic-auth). ## Related links - [`grpc:LdapUserStoreConfig` record - API documentation](https://lib.ballerina.io/ballerina/grpc/latest#LdapUserStoreConfig) - [gRPC service basic authentication LDAP user store - Specification](/spec/grpc/#5112-service---basic-auth---ldap-user-store) - [`auth` module - API documentation](https://lib.ballerina.io/ballerina/auth/latest/) ================================================ FILE: examples/grpc-service-basic-auth-ldap-user-store/grpc_service_basic_auth_ldap_user_store.metatags ================================================ description: BBE on how to secure gRPC service with Basic Auth in Ballerina. keywords: ballerina, ballerina by example, bbe, grpc, auth, basic auth ================================================ FILE: examples/grpc-service-basic-auth-ldap-user-store/grpc_service_basic_auth_ldap_user_store.server.out ================================================ $ bal run service ================================================ FILE: examples/grpc-service-basic-auth-ldap-user-store/grpc_simple.proto ================================================ // This is the service definition of the simple scenario. syntax = "proto3"; import "google/protobuf/wrappers.proto"; service HelloWorld { rpc hello (google.protobuf.StringValue) returns (google.protobuf.StringValue); } ================================================ FILE: examples/grpc-service-bidirectional-streaming/grpc_bidirectional_streaming.md ================================================ # gRPC service - Bidirectional streaming RPC A `grpc:Listener` is created by providing the port and a `grpc:Service` is attached to it. In the bidirectional streaming scenario, once a client is connected to the service, the client and the service send message streams to each other. In this scenario, the two streams operate independently, and therefore, the clients and servers can read and write in any order. Use this to receive multiple request messages from a client and send multiple response messages back. ## Generate the service definition 1. Create a new Protocol Buffers definition file `grpc_bidirectional_streaming.proto` and add the service definition below. ::: code grpc_bidirectional_streaming.proto ::: 2. Run the command below in the Ballerina tools distribution for stub generation. ::: code grpc_bidirectional_streaming.out ::: Once you run the command, the `grpc_bidirectional_streaming_pb.bal` file gets generated inside the `stubs` directory. ## Implement and run the service 1. Create a Ballerina package (e.g., `service`). Delete the `main.bal` file created by default as it is not required for this example. 2. Copy the generated `grpc_bidirectional_streaming_pb.bal` file from the `stubs` directory to the `service` package. 3. Create a new `grpc_bidirectional_streaming_service.bal` file inside the `service` package and add the service implementation below. ::: code grpc_bidirectional_streaming_service.bal ::: 4. Run the service by executing the command below. ::: out grpc_bidirectional_streaming_service.out ::: >**Tip:** You can invoke the above service via the [gRPC client - Bidirectional streaming RPC](/learn/by-example/grpc-client-bidirectional-streaming/). ## Related links - [`grpc` module - API documentation](https://lib.ballerina.io/ballerina/grpc/latest) - [gRPC service bidirectional streaming - Specification](/spec/grpc/#44-bidirectional-streaming-rpc) - [gRPC tool](/learn/grpc-tool/) ================================================ FILE: examples/grpc-service-bidirectional-streaming/grpc_bidirectional_streaming.metatags ================================================ description: This Ballerina by Example has a gRPC streaming service for bidirectional streaming RPC. keywords: ballerina, ballerina by example, bbe, grpc, bidirectional streaming, service ================================================ FILE: examples/grpc-service-bidirectional-streaming/grpc_bidirectional_streaming.out ================================================ $ bal grpc --input grpc_bidirectional_streaming.proto --output stubs ================================================ FILE: examples/grpc-service-bidirectional-streaming/grpc_bidirectional_streaming.proto ================================================ // This is the service definition for the bidirectional streaming scenario. syntax = "proto3"; import "google/protobuf/wrappers.proto"; service Chat { rpc chat (stream ChatMessage) returns (stream google.protobuf.StringValue); } message ChatMessage { string name = 1; string message = 2; } ================================================ FILE: examples/grpc-service-bidirectional-streaming/grpc_bidirectional_streaming_service.bal ================================================ import ballerina/grpc; import ballerina/log; @grpc:Descriptor { value: GRPC_BIDIRECTIONAL_STREAMING_DESC } service "Chat" on new grpc:Listener(9090) { // The generated code of the Ballerina gRPC command does not contain ChatStringCaller. // To show the usage of a caller, this RPC call uses a caller to send messages to the client. remote function chat(ChatStringCaller caller, stream clientStream) { // Reads and processes each message in the client stream. do { _ = check from ChatMessage chatMsg in clientStream do { checkpanic caller->sendString(string `${chatMsg.name}: ${chatMsg.message}`); }; // Once the client sends a notification to indicate the end of the stream, // '()' is returned by the stream. check caller->complete(); } on fail error err { log:printError("The connection is closed with an error.", 'error = err); } } } ================================================ FILE: examples/grpc-service-bidirectional-streaming/grpc_bidirectional_streaming_service.out ================================================ $ bal run service ================================================ FILE: examples/grpc-service-bidirectional-streaming/tests/grpc_bidirectional_streaming_test.bal ================================================ // This is the Ballerina test for bidirectional streaming scenario. import ballerina/test; //Client endpoint configuration. ChatClient chatEp = check new("http://localhost:9090"); @test:Config function testBidiStreamingService() returns error? { // Executes the RPC call and receives the customized streaming client. ChatStreamingClient streamingClient = check chatEp->chat(); // Reads the server responses in another strand. future f1 = start readResponseTest(streamingClient); // Sends multiple messages to the server. ChatMessage[] messages = [ {name: "Sam", message: "Hi"}, {name: "Ann", message: "Hey"}, {name: "John", message: "Hello"} ]; foreach ChatMessage msg in messages { check streamingClient->sendChatMessage(msg); } // Once all the messages are sent, the client sends the message to notify the server about the completion. check streamingClient->complete(); // Waits until all server messages are received. check wait f1; } function readResponseTest(ChatStreamingClient streamingClient) returns error? { string expectedMsg1 = "Sam: Hi"; string expectedMsg2 = "Ann: Hey"; string expectedMsg3 = "John: Hello"; // Receives the server stream response iteratively. string? result = check streamingClient->receiveString(); while !(result is ()) { test:assertTrue(result == expectedMsg1 || result == expectedMsg2 || result == expectedMsg3); result = check streamingClient->receiveString(); } } ================================================ FILE: examples/grpc-service-check-deadline/grpc_service_check_deadline.bal ================================================ import ballerina/grpc; import ballerina/protobuf.types.wrappers; @grpc:Descriptor { value: GRPC_SIMPLE_DESC } service "HelloWorld" on new grpc:Listener(9090) { remote function hello(wrappers:ContextString request) returns string|error { // Check if the deadline has been exceeded and response accordingly. boolean isCancelled = check grpc:isCancelled(request.headers); if isCancelled { return error grpc:DeadlineExceededError("Exceeded the configured deadline"); } return "Hello"; } } ================================================ FILE: examples/grpc-service-check-deadline/grpc_service_check_deadline.md ================================================ # gRPC service - Check deadline The `grpc:Service` allows to check whether a deadline has been exceeded in a client connection. Deadlines allow gRPC clients to specify how long they are willing to wait for an RPC to complete. The deadline is checked in the service using the `isCancelled` method and a `grpc:DeadlineExceededError` is returned if it is exceeded. Use this to check the upper limit on how long a call can run. ::: code grpc_service_check_deadline.bal ::: Setting up the service is the same as setting up the simple RPC service with additional configurations. For information on implementing the service, see [gRPC service - Simple RPC](/learn/by-example/grpc-service-simple/). Run the service by executing the command below. ::: out grpc_service_check_deadline.out ::: >**Tip:** You can invoke the above service via the [gRPC client - Set deadline](/learn/by-example/grpc-client-set-deadline/). ## Related links - [`grpc:isCancelled` function - API documentation](https://lib.ballerina.io/ballerina/grpc/latest#isCancelled) - [gRPC service check deadline - Specification](/spec/grpc/#61-grpc-deadline) ================================================ FILE: examples/grpc-service-check-deadline/grpc_service_check_deadline.metatags ================================================ description: This Ballerina by Example has a gRPC service with a deadline check. keywords: ballerina, ballerina by example, bbe, grpc, unary, service, deadline ================================================ FILE: examples/grpc-service-check-deadline/grpc_service_check_deadline.out ================================================ $ bal run service ================================================ FILE: examples/grpc-service-check-deadline/grpc_simple.proto ================================================ // This is the service definition of the simple scenario. syntax = "proto3"; import "google/protobuf/wrappers.proto"; service HelloWorld { rpc hello (google.protobuf.StringValue) returns (google.protobuf.StringValue); } ================================================ FILE: examples/grpc-service-client-streaming/grpc_client_streaming.md ================================================ # gRPC service - Client-side streaming RPC A `grpc:Listener` is created by providing the port and a `grpc:Service` is attached to it. In the client streaming scenario, once a client is connected to the service, the client sends a message stream to the server. Once the client completes the request message, the server sends the response message to complete the call. Use this to receive multiple request messages from a client and send a single response message back. ## Generate the service definition 1. Create new Protocol Buffers definition file `grpc_client_streaming.proto` and add service definition below. ::: code grpc_client_streaming.proto ::: 2. Run the command below in the Ballerina tools distribution for stub generation. ::: out grpc_client_streaming.out ::: Once you run the command, the `grpc_client_streaming_pb.bal` file gets generated inside the stubs directory. ## Implement and run the service 1. Create a Ballerina package (e.g., `service`). 2. Copy the generated `grpc_client_streaming_pb.bal` file from the `stubs` directory to the `service` package. 3. Create a new `grpc_client_streaming.bal` file inside the `service` package and add the service implementation below. ::: code grpc_client_streaming_service.bal ::: 4. Run the service by executing the command below. ::: out grpc_client_streaming_service.out ::: >**Tip:** You can invoke the above service via the [gRPC client - Client-side streaming RPC](/learn/by-example/grpc-client-client-streaming/). ## Related links - [`grpc` module - API documentation](https://lib.ballerina.io/ballerina/grpc/latest) - [gRPC service client-side streaming - Specification](/spec/grpc/#43-client-streaming-rpc) - [gRPC tool](/learn/grpc-tool/) ================================================ FILE: examples/grpc-service-client-streaming/grpc_client_streaming.metatags ================================================ description: This Ballerina by Example has a gRPC service for client streaming RPC. keywords: ballerina, ballerina by example, bbe, grpc, client streaming, service ================================================ FILE: examples/grpc-service-client-streaming/grpc_client_streaming.out ================================================ $ bal grpc --input grpc_client_streaming.proto --output stubs ================================================ FILE: examples/grpc-service-client-streaming/grpc_client_streaming.proto ================================================ // This is the service definition for the client streaming scenario. syntax = "proto3"; import "google/protobuf/wrappers.proto"; service HelloWorld { rpc lotsOfGreetings (stream google.protobuf.StringValue) returns (google.protobuf.StringValue); } ================================================ FILE: examples/grpc-service-client-streaming/grpc_client_streaming_service.bal ================================================ import ballerina/grpc; import ballerina/log; @grpc:Descriptor { value: GRPC_CLIENT_STREAMING_DESC } service "HelloWorld" on new grpc:Listener(9090) { remote function lotsOfGreetings(stream clientStream) returns string { // Reads and processes each message in the client stream. error? result = from string name in clientStream do { log:printInfo(string `Greet received: ${name}`); }; if (result is error) { // Client closes the connection with an error. log:printError("The connection is closed with an error.", 'error = result); } // Once the client sends a notification to indicate the end of the stream, // '()' is returned by the stream. return "Ack"; } } ================================================ FILE: examples/grpc-service-client-streaming/grpc_client_streaming_service.out ================================================ $ bal run service time = 2022-11-17T17:10:14.503+05:30 level = INFO module = foo/test message = "Greet received: Hi Sam" time = 2022-11-17T17:10:14.512+05:30 level = INFO module = foo/test message = "Greet received: Hey Sam" time = 2022-11-17T17:10:14.513+05:30 level = INFO module = foo/test message = "Greet received: GM Sam" ================================================ FILE: examples/grpc-service-client-streaming/tests/grpc_client_streaming_test.bal ================================================ // This is the Ballerina test for the client streaming scenario. import ballerina/test; // Client endpoint configuration. HelloWorldClient helloWorldEp = check new("http://localhost:9090"); @test:Config function testClientStreamingService() returns error? { // Executes the client-streaming RPC call and receives the streaming client. LotsOfGreetingsStreamingClient streamingClient = check helloWorldEp->lotsOfGreetings(); string[] requests = ["Hi Sam", "Hey Sam", "GM Sam"]; // Sends multiple messages to the server. string[] greets = ["Hi", "Hey", "GM"]; foreach var greet in requests { check streamingClient->sendString(greet); } // Once all the messages are sent, the server notifies the caller with a `complete` message. check streamingClient->complete(); string? response = check streamingClient->receiveString(); string expected = "Ack"; test:assertEquals(response is () ? "" : response, expected); } ================================================ FILE: examples/grpc-service-headers/grpc_simple.proto ================================================ // This is the service definition of the simple scenario. syntax = "proto3"; import "google/protobuf/wrappers.proto"; service HelloWorld { rpc hello (google.protobuf.StringValue) returns (google.protobuf.StringValue); } ================================================ FILE: examples/grpc-service-headers/grpc_simple_with_headers.md ================================================ # gRPC service - Send/Receive headers The `grpc:Service` allows receiving headers and sending headers from/to a gRPC server. The gRPC - Protobuf CLI tool generates a `Context` record for each Protobuf message type, which contains the Protobuf message and the header map. The header map supports `string`and `string[]` types. The `Context` type of the required record is provided as the target type of the remote method to receive the headers. A `Context` record value is created with the required headers and is returned to the client. The `getHeader` and `getHeaders` methods are also available to manipulate the header values. ::: code grpc_simple_with_headers_service.bal ::: Setting up the service is the same as setting up the simple RPC service with input and output parameter change. For information on implementing the service, see [gRPC service - Simple RPC](/learn/by-example/grpc-service-simple/). Run the service by executing the command below. ::: out grpc_simple_with_headers_service.out ::: >**Tip:** You can invoke the above service via the [gRPC client - Send/Receive headers](/learn/by-example/grpc-client-headers/). ## Related links - [`grpc` module - API documentation](https://lib.ballerina.io/ballerina/grpc/latest) - [`grpc` module - Specification](/spec/grpc/) ================================================ FILE: examples/grpc-service-headers/grpc_simple_with_headers.metatags ================================================ description: This Ballerina by Example has a gRPC simple service sends and receives messages and headers through a remote call. keywords: ballerina, ballerina by example, bbe, grpc, unary, simple, service, headers, metadata ================================================ FILE: examples/grpc-service-headers/grpc_simple_with_headers_service.bal ================================================ import ballerina/grpc; import ballerina/log; import ballerina/protobuf.types.wrappers; @grpc:Descriptor { value: GRPC_SIMPLE_DESC } service "HelloWorld" on new grpc:Listener(9090) { remote function hello(wrappers:ContextString request) returns wrappers:ContextString|error { // Reads the request message and creates a response. string message = "Hello " + request.content; // Reads the header value in the request message by passing the request header // map and header key. string reqHeader = check grpc:getHeader(request.headers, "client_header_key"); log:printInfo("Server received header value: " + reqHeader); // Sends the response with the header. return {content: message, headers: {server_header_key: "Response Header value"}}; } } ================================================ FILE: examples/grpc-service-headers/grpc_simple_with_headers_service.out ================================================ $ bal run service time = 2022-11-17T11:55:59.543+05:30 level = INFO module = foo/test message = "Server received header value: 0987654321" ================================================ FILE: examples/grpc-service-headers/tests/grpc_simple_with_headers_test.bal ================================================ // This is the Ballerina test for simple RPC with headers scenario. import ballerina/grpc; import ballerina/test; import ballerina/protobuf.types.wrappers; // Client endpoint configuration. HelloWorldClient clientEp = check new("http://localhost:9090"); @test:Config function testSimpleServiceWithHeaders() returns error? { // Creates the request message with the header value. wrappers:ContextString requestMessage = {content: "WSO2", headers: {client_header_key: "0987654321"}}; // Executes a simple remote call. wrappers:ContextString result = check clientEp->helloContext(requestMessage); // Reads the content in the response message. string expected = "Hello WSO2"; test:assertEquals(result.content, expected); // Reads the header value in the response message. string headerValue = check grpc:getHeader(result.headers, "server_header_key"); string expectedHeaderValue = "Response Header value"; test:assertEquals(headerValue, expectedHeaderValue); } ================================================ FILE: examples/grpc-service-jwt-auth/grpc_service_jwt_auth.bal ================================================ import ballerina/grpc; listener grpc:Listener securedEP = new (9090, secureSocket = { key: { certFile: "../resource/path/to/public.crt", keyFile: "../resource/path/to/private.key" } } ); @grpc:ServiceConfig { auth: [ { jwtValidatorConfig: { issuer: "wso2", audience: "ballerina", signatureConfig: { certFile: "../resource/path/to/public.crt" }, scopeKey: "scp" }, scopes: ["admin"] } ] } @grpc:Descriptor { value: GRPC_SIMPLE_DESC } service "HelloWorld" on securedEP { remote function hello(string request) returns string { return "Hello " + request; } } ================================================ FILE: examples/grpc-service-jwt-auth/grpc_service_jwt_auth.md ================================================ # gRPC service - JWT authentication The `grpc:Service` can be secured with JWT and additionally, scopes can be added to enforce authorization. It validates the JWT sent in the `Authorization` metadata against the provided configurations. Ballerina uses the concept of scopes for authorization. The scope can be included in the JWT using a custom claim attribute. That custom claim attribute also can be configured as the `scopeKey`. In the authorization phase, the scopes of the service are compared against the scope included in the JWT for at least one match between the two sets. ::: code grpc_service_jwt_auth.bal ::: Setting up the service is the same as setting up the simple RPC service with additional configurations. For information on implementing the service, see [gRPC service - Simple RPC](/learn/by-example/grpc-service-simple/). Run the service by executing the command below. ::: out grpc_service_jwt_auth.server.out ::: >**Tip:** You can invoke the above service via the [gRPC client - Self signed JWT authentication](/learn/by-example/grpc-client-self-signed-jwt-auth). ## Related links - [`grpc:JwtValidatorConfig` record - API documentation](https://lib.ballerina.io/ballerina/grpc/latest#JwtValidatorConfig) - [gRPC service JWT authentication - Specification](/spec/grpc/#5113-service---jwt-auth) - [`jwt` package - API documentation](https://lib.ballerina.io/ballerina/jwt/latest/) ================================================ FILE: examples/grpc-service-jwt-auth/grpc_service_jwt_auth.metatags ================================================ description: BBE on how to secure gRPC service with JWT Auth in Ballerina. keywords: ballerina, ballerina by example, bbe, grpc, auth, jwt auth ================================================ FILE: examples/grpc-service-jwt-auth/grpc_service_jwt_auth.server.out ================================================ $ bal run service ================================================ FILE: examples/grpc-service-jwt-auth/grpc_simple.proto ================================================ // This is the service definition of the simple scenario. syntax = "proto3"; import "google/protobuf/wrappers.proto"; service HelloWorld { rpc hello (google.protobuf.StringValue) returns (google.protobuf.StringValue); } ================================================ FILE: examples/grpc-service-mutual-ssl/grpc_service_mutual_ssl.bal ================================================ import ballerina/grpc; // The gRPC listener can be configured to accept new connections that are secured via mutual SSL. listener grpc:Listener securedEP = new (9090, secureSocket = { key: { certFile: "../resource/path/to/public.crt", keyFile: "../resource/path/to/private.key" }, // Enables mutual SSL. mutualSsl: { verifyClient: grpc:REQUIRE, cert: "../resource/path/to/public.crt" } } ); @grpc:Descriptor { value: GRPC_SIMPLE_DESC } service "HelloWorld" on securedEP { remote function hello(string request) returns string { return "Hello " + request; } } ================================================ FILE: examples/grpc-service-mutual-ssl/grpc_service_mutual_ssl.md ================================================ # gRPC service - Mutual SSL The `grpc:Listener` with mutual SSL (mTLS) enabled in it allows exposing a connection secured with mutual SSL, which is a certificate-based authentication process in which two parties (the client and server) authenticate each other by verifying the digital certificates. It ensures that both parties are assured of each other's identity. The `grpc:Listener` secured with mutual SSL is created by providing the `secureSocket` configurations, which require `grpc:REQUIRE` as the `verifyClient`, the server's public certificate as the `certFile`, the server's private key as the `keyFile`, and the client's certificate as the `cert`. Use this to secure the gRPC connection with mutual SSL. ::: code grpc_service_mutual_ssl.bal ::: Setting up the service is the same as setting up the simple RPC service with additional configurations. For information on implementing the service, see [gRPC service - Simple RPC](/learn/by-example/grpc-service-simple/). Run the service by executing the command below. ::: out grpc_service_mutual_ssl.server.out ::: >**Tip:** You can invoke the above service via the [gRPC client - Mutual SSL](/learn/by-example/grpc-client-mutual-ssl/). ## Related links - [`grpc:ListenerSecureSocket` record - API documentation](https://lib.ballerina.io/ballerina/grpc/latest#ListenerSecureSocket) - [gRPC service mutual SSL - Specification](/spec/grpc/#52-ssltls-and-mutual-ssl) ================================================ FILE: examples/grpc-service-mutual-ssl/grpc_service_mutual_ssl.metatags ================================================ description: BBE on how to secure gRPC listener with mutual SSL. keywords: ballerina, ballerina by example, bbe, grpc, mutual ssl, ssl protocols, ciphers ================================================ FILE: examples/grpc-service-mutual-ssl/grpc_service_mutual_ssl.server.out ================================================ $ bal run service ================================================ FILE: examples/grpc-service-mutual-ssl/grpc_simple.proto ================================================ // This is the service definition of the simple scenario. syntax = "proto3"; import "google/protobuf/wrappers.proto"; service HelloWorld { rpc hello (google.protobuf.StringValue) returns (google.protobuf.StringValue); } ================================================ FILE: examples/grpc-service-oauth2/grpc_service_oauth2.bal ================================================ import ballerina/grpc; listener grpc:Listener securedEP = new (9090, secureSocket = { key: { certFile: "../resource/path/to/public.crt", keyFile: "../resource/path/to/private.key" } } ); @grpc:ServiceConfig { auth: [ { oauth2IntrospectionConfig: { url: "https://localhost:9445/oauth2/introspect", tokenTypeHint: "access_token", scopeKey: "scp", clientConfig: { customHeaders: {"Authorization": "Basic YWRtaW46YWRtaW4="}, secureSocket: { cert: "../resource/path/to/public.crt" } } }, scopes: ["admin"] } ] } @grpc:Descriptor { value: GRPC_SIMPLE_DESC } service "HelloWorld" on securedEP { remote function hello(string request) returns string { return "Hello " + request; } } ================================================ FILE: examples/grpc-service-oauth2/grpc_service_oauth2.md ================================================ # gRPC service - OAuth2 A gRPC service can be secured with OAuth2 and additionally, scopes can be added to enforce fine-grained authorization. It validates the OAuth2 token sent in the `Authorization` metadata against the provided configurations. This calls the configured introspection endpoint to validate. Ballerina uses the concept of scopes for authorization. The scope can be included in the introspection response using a custom claim attribute. That custom claim attribute also can be configured as the `scopeKey`. In the authorization phase, the scopes of the service are compared against the scope included in the introspection response for at least one match between the two sets. ::: code grpc_service_oauth2.bal ::: Setting up the service is the same as setting up the simple RPC service with additional configurations. For information on implementing the service, see [gRPC service - Simple RPC](/learn/by-example/grpc-service-simple/). Run the service by executing the command below. ::: out grpc_service_oauth2.server.out ::: >**Tip:** You can invoke the above service via the clients below. - [gRPC client - OAuth2 client credentials grant type](/learn/by-example/grpc-client-oauth2-client-credentials-grant-type) - [gRPC client - OAuth2 password grant type](/learn/by-example/grpc-client-oauth2-password-grant-type) - [gRPC client - OAuth2 refresh token grant type](/learn/by-example/grpc-client-oauth2-refresh-token-grant-type) - [gRPC client - OAuth2 JWT bearer grant type](/learn/by-example/grpc-client-oauth2-jwt-bearer-grant-type) ## Related links - [`grpc:OAuth2IntrospectionConfig` record - API documentation](https://lib.ballerina.io/ballerina/grpc/latest#OAuth2IntrospectionConfig) - [gRPC service OAuth2 - Specification](/spec/grpc/#5114-service---oauth2) - [`oauth2` module - API documentation](https://lib.ballerina.io/ballerina/oauth2/latest/) ================================================ FILE: examples/grpc-service-oauth2/grpc_service_oauth2.metatags ================================================ description: BBE on how to secure gRPC service with OAuth2 in Ballerina. keywords: ballerina, ballerina by example, bbe, grpc, auth, oauth2, introspection ================================================ FILE: examples/grpc-service-oauth2/grpc_service_oauth2.server.out ================================================ $ bal run service ================================================ FILE: examples/grpc-service-oauth2/grpc_simple.proto ================================================ // This is the service definition of the simple scenario. syntax = "proto3"; import "google/protobuf/wrappers.proto"; service HelloWorld { rpc hello (google.protobuf.StringValue) returns (google.protobuf.StringValue); } ================================================ FILE: examples/grpc-service-server-streaming/grpc_server_streaming.md ================================================ # gRPC service - Server-side streaming RPC A `grpc:Listener` is created by providing the port and a `grpc:Service` is attached to it. In the server streaming scenario, once a client is connected to the service and sends a request message, the service sends multiple messages to the client. Use this to receive a single request message from a client and send multiple response messages back. ## Generate the service definition 1. Create new Protocol Buffers definition file `grpc_server_streaming.proto` and add service definition below. ::: code grpc_server_streaming.proto ::: 2. Run the command below in the Ballerina tools distribution for stub generation. ::: out grpc_server_streaming.out ::: Once you run the command, the `grpc_server_streaming_pb.bal` file gets generated inside the stubs directory. ## Implement and run the service 1. Create a Ballerina package (e.g., `service`). Delete the `main.bal` file created by default as it is not required for this example. 2. Copy the generated `grpc_server_streaming_pb.bal` file from the `stubs` directory to the `service` package. 3. Create a new `grpc_server_streaming.bal` file inside the `service` package and add the service implementation below. ::: code grpc_server_streaming_service.bal ::: 4. Run the service by executing the command below. ::: out grpc_server_streaming_service.out ::: >**Tip:** You can invoke the above service via the [gRPC client - Server-side streaming RPC](/learn/by-example/grpc-client-server-streaming/). ## Related links - [`grpc` module - API documentation](https://lib.ballerina.io/ballerina/grpc/latest) - [gRPC service server-side streaming - Specification](/spec/grpc/#42-server-streaming-rpc) - [gRPC tool](/learn/grpc-tool/) ================================================ FILE: examples/grpc-service-server-streaming/grpc_server_streaming.metatags ================================================ description: This Ballerina by Example has a gRPC streaming service for server streaming RPC. keywords: ballerina, ballerina by example, bbe, grpc, server streaming, service ================================================ FILE: examples/grpc-service-server-streaming/grpc_server_streaming.out ================================================ $ bal grpc --input grpc_server_streaming.proto --output stubs ================================================ FILE: examples/grpc-service-server-streaming/grpc_server_streaming.proto ================================================ // This is the service definition for the server streaming scenario. syntax = "proto3"; import "google/protobuf/wrappers.proto"; service HelloWorld { rpc lotsOfReplies (google.protobuf.StringValue) returns (stream google.protobuf.StringValue); } ================================================ FILE: examples/grpc-service-server-streaming/grpc_server_streaming_service.bal ================================================ import ballerina/grpc; @grpc:Descriptor { value: GRPC_SERVER_STREAMING_DESC } service "HelloWorld" on new grpc:Listener(9090) { remote function lotsOfReplies(string name) returns stream { string[] greets = ["Hi", "Hey", "GM"]; // Creates the array of responses by appending the received name. int i = 0; foreach string greet in greets { greets[i] = greet + " " + name; i += 1; } // Returns the stream of messages back to the client. return greets.toStream(); } } ================================================ FILE: examples/grpc-service-server-streaming/grpc_server_streaming_service.out ================================================ $ bal run service ================================================ FILE: examples/grpc-service-server-streaming/tests/grpc_server_streaming_test.bal ================================================ // This is the Ballerina test for the server streaming scenario. import ballerina/grpc; import ballerina/test; // Client endpoint configuration. HelloWorldClient streamingEp = check new("http://localhost:9090"); @test:Config function testServerStreamingService() returns error? { // Executes the streaming RPC call and gets the response as a stream. stream result = check streamingEp->lotsOfReplies("Sam"); string expectedMsg1 = "Hi Sam"; string expectedMsg2 = "Hey Sam"; string expectedMsg3 = "GM Sam"; // Iterates through the stream and prints the content. check result.forEach(function(string msg) { test:assertTrue(msg == expectedMsg1 || msg == expectedMsg2 || msg == expectedMsg3); }); } ================================================ FILE: examples/grpc-service-simple/grpc_service_simple.bal ================================================ import ballerina/grpc; @grpc:Descriptor { value: GRPC_SERVICE_SIMPLE_DESC } service "HelloWorld" on new grpc:Listener(9090) { remote function hello(string request) returns string { // Reads the request message and sends a response. return "Hello " + request; } } ================================================ FILE: examples/grpc-service-simple/grpc_service_simple.md ================================================ # gRPC service - Simple RPC A `grpc:Listener` is created by providing the port and a `grpc:Service` is attached to it. In the simple RPC scenario, once a client is connected to the service and sends a request message, the service sends a single response message to the client. Use this to receive a single request message from a client and send a single response message back. ## Generate the service definition 1. Create a new Protocol Buffers definition file named `grpc_simple.proto` and add the service definition below. ::: code grpc_service_simple.proto ::: 2. Run the command below from the Ballerina tools distribution for stub generation. ::: out grpc_simple.out ::: Once you run the command, the `grpc_simple_pb.bal` file gets generated inside the `stubs` directory. ## Implement and run the service 1. Create a Ballerina package (e.g., `service`). Delete the `main.bal` file created by default as it is not required for this example. 2. Copy the generated `grpc_simple_pb.bal` stub file from the `stubs` directory to the `service` package. 3. Create a new `grpc_simple_service.bal` file inside the `service` package and add the service implementation below. ::: code grpc_service_simple.bal ::: 4. Execute the commands below to run the service. ::: out grpc_service_simple.out ::: >**Tip:** You can invoke the above service via the [gRPC client - Simple RPC](/learn/by-example/grpc-client-simple/). ## Related links - [`grpc` module - API documentation](https://lib.ballerina.io/ballerina/grpc/latest) - [gRPC service simple RPC - Specification](/spec/grpc/#41-simple-rpc) - [gRPC tool](/learn/grpc-tool/) ================================================ FILE: examples/grpc-service-simple/grpc_service_simple.metatags ================================================ description: This Ballerina by Example has a gRPC service for simple RPC. keywords: ballerina, ballerina by example, bbe, grpc, unary, simple, service ================================================ FILE: examples/grpc-service-simple/grpc_service_simple.out ================================================ $ bal run service ================================================ FILE: examples/grpc-service-simple/grpc_service_simple.proto ================================================ syntax = "proto3"; import "google/protobuf/wrappers.proto"; service HelloWorld { rpc hello (google.protobuf.StringValue) returns (google.protobuf.StringValue); } ================================================ FILE: examples/grpc-service-simple/grpc_simple.out ================================================ $ bal grpc --input grpc_simple.proto --output stubs ================================================ FILE: examples/grpc-service-simple/tests/grpc_simple_test.bal ================================================ // This is the Ballerina test for the simple GRPC scenario. import ballerina/test; // Client endpoint configuration. HelloWorldClient clientEp = check new("http://localhost:9090"); @test:Config function testSimpleService() returns error? { // Executes a simple remote call. string result = check clientEp->hello("WSO2"); string expected = "Hello WSO2"; test:assertEquals(result, expected); } ================================================ FILE: examples/grpc-service-ssl-tls/grpc_service_ssl_tls.bal ================================================ import ballerina/grpc; listener grpc:Listener securedEP = new (9090, secureSocket = { key: { certFile: "./resources/public.crt", keyFile: "./resources/private.key" } } ); @grpc:Descriptor { value: GRPC_SIMPLE_DESC } service "HelloWorld" on securedEP { remote function hello(string request) returns string { return "Hello " + request; } } ================================================ FILE: examples/grpc-service-ssl-tls/grpc_service_ssl_tls.md ================================================ # gRPC service - SSL/TLS The `grpc:Listener` can be configured to communicate with a gRPC client via SSL/TLS by providing a certificate file and a private key file. The certificate and the key can be provided through the `secureSocket` field of the listener configuration. Use this to secure the communication and data transfer between the server and the client. ::: code grpc_service_ssl_tls.bal ::: Setting up the service is the same as setting up the simple RPC service with additional configurations. For information on implementing the service, see [gRPC service - Simple RPC](/learn/by-example/grpc-service-simple/). Run the service by executing the command below. ::: out grpc_service_ssl_tls.server.out ::: >**Tip:** You can invoke the above service via the [gRPC client - SSL/TLS](/learn/by-example/grpc-client-ssl-tls/). ## Related links - [`grpc:ListenerSecureSocket` record - API documentation](https://lib.ballerina.io/ballerina/grpc/latest#ListenerSecureSocket) - [gRPC service SSL/TLS - Specification](/spec/grpc/#52-ssltls-and-mutual-ssl) ================================================ FILE: examples/grpc-service-ssl-tls/grpc_service_ssl_tls.metatags ================================================ description: BBE on how to secure gRPC listener with SSL. keywords: ballerina, ballerina by example, bbe, grpc, ssl, tls ================================================ FILE: examples/grpc-service-ssl-tls/grpc_service_ssl_tls.server.out ================================================ $ bal run service ================================================ FILE: examples/grpc-service-ssl-tls/grpc_simple.proto ================================================ // This is the service definition of the simple scenario. syntax = "proto3"; import "google/protobuf/wrappers.proto"; service HelloWorld { rpc hello (google.protobuf.StringValue) returns (google.protobuf.StringValue); } ================================================ FILE: examples/hello-world/hello_world.bal ================================================ import ballerina/io; public function main() { io:println("Hello, World!"); } ================================================ FILE: examples/hello-world/hello_world.md ================================================ # Hello world main Let's write the `Hello World` program in Ballerina. ::: code hello_world.bal ::: To run this sample, place the source code in a file named `hello_world.bal` and use the `bal run` command. ::: out hello_world.out ::: ================================================ FILE: examples/hello-world/hello_world.metatags ================================================ description: The classic "Hello, World" program in Ballerina keywords: ballerina, ballerina by example, bbe, ================================================ FILE: examples/hello-world/hello_world.out ================================================ $ bal run hello_world.bal Hello, World! ================================================ FILE: examples/hello-world-service/hello_world_service.bal ================================================ import ballerina/http; service / on new http:Listener(9090) { // This function responds with `string` value `Hello, World!` to HTTP GET requests. resource function get greeting() returns string { return "Hello, World!"; } } ================================================ FILE: examples/hello-world-service/hello_world_service.client.out ================================================ $ curl http://localhost:9090/greeting Hello, World! ================================================ FILE: examples/hello-world-service/hello_world_service.md ================================================ # Hello world service Let's write a simple HTTP service in Ballerina. This example demonstrates the network primitives in the language that make it simpler to develop services. ::: code hello_world_service.bal ::: Run the service as follows. ::: out hello_world_service.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out hello_world_service.client.out ::: ================================================ FILE: examples/hello-world-service/hello_world_service.metatags ================================================ description: The Hello World HTTP service in Ballerina keywords: ballerina, ballerina by example, bbe, ================================================ FILE: examples/hello-world-service/hello_world_service.server.out ================================================ $ bal run hello_world_service.bal ================================================ FILE: examples/hierarchical-resources/hierarchical_resources.bal ================================================ import ballerina/http; // Base path of this service is `/demo`. service /demo on new http:Listener(8080) { // You can combine the base path and the relative path to get the path of the resource (i.e., `/demo/greeting/hello`). resource function get greeting/hello(string name) returns string { return "Hello, " + name; } } ================================================ FILE: examples/hierarchical-resources/hierarchical_resources.client.out ================================================ $ curl "localhost:8080/demo/greeting/hello?name=Ballerina" Hello, Ballerina ================================================ FILE: examples/hierarchical-resources/hierarchical_resources.md ================================================ # Hierarchical resources Resource name is relative path, which can have multiple path segments. Base path is absolute path. A listener can have multiple services each with different base paths. ::: code hierarchical_resources.bal ::: Run the service using the `bal run` command. ::: out hierarchical_resources.server.out ::: Run this cURL command to invoke the resource. ::: out hierarchical_resources.client.out ::: ================================================ FILE: examples/hierarchical-resources/hierarchical_resources.metatags ================================================ description: This BBE introduces hierarchical resources concept. keywords: ballerina, ballerina by example, bbe, resources, hierarchical resources, services ================================================ FILE: examples/hierarchical-resources/hierarchical_resources.server.out ================================================ $ bal run hierarchical_resources.bal ================================================ FILE: examples/http-1-1-to-2-0-protocol-switch/http_1_1_to_2_0_protocol_switch.md ================================================ ================================================ FILE: examples/http-100-continue/http_100_continue.bal ================================================ import ballerina/http; import ballerina/log; service / on new http:Listener(9090) { resource function post greeting(http:Caller caller, http:Request request) returns error? { // Check if the client expects a 100-continue response. if request.expects100Continue() { string mediaType = request.getContentType(); if mediaType.toLowerAscii() == "text/plain" { // Send a `100-continue` response to the client. check caller->continue(); } else { // Send a `417` response to ignore the payload as the content type is mismatched // with the expected content type. http:ExpectationFailed resp = {body: "Unprocessable Entity"}; return caller->respond(resp); } } // The client starts sending the payload once it receives the // `100-continue` response. The payload that is sent by the client is retrieved. var payload = request.getTextPayload(); if payload is string { log:printInfo(payload); check caller->respond("Hello World!\n"); } else { http:InternalServerError resp = {body: payload.message()}; check caller->respond(resp); } } } ================================================ FILE: examples/http-100-continue/http_100_continue.client.out ================================================ $ curl -v -d "TEST 100 CONTINUE" http://localhost:9090/greeting -H 'Expect:100-continue' -H 'Content-Type: text/plain' * Trying 127.0.0.1... * Connected to localhost (127.0.0.1) port 9090 (#0) > POST /greeting HTTP/1.1 > Host: localhost:9090 > User-Agent: curl/7.64.1 > Accept: */* > Expect:100-continue > Content-Type: text/plain > Content-Length: 17 > < HTTP/1.1 100 Continue < server: ballerina < date: Tue, 20 Dec 2022 17:01:05 +0530 * We are completely uploaded and fine < HTTP/1.1 201 Created < content-type: text/plain < content-length: 13 < server: ballerina < date: Tue, 20 Dec 2022 17:01:05 +0530 < Hello World! * Connection #0 to host localhost left intact # Use the following client to invoke the service using an unsupported media type. The service is supposed to ignore # the payload if the content type does not match. $ curl -v -d '{"TEST":"100 CONTINUE"}' http://localhost:9090/greeting -H 'Expect:100-continue' -H 'Content-Type: application/json' * Connected to localhost (::1) port 9090 (#0) > POST /greeting HTTP/1.1 > Host: localhost:9090 > User-Agent: curl/7.64.1 > Accept: */* > Expect:100-continue > Content-Type: application/json > Content-Length: 23 > < HTTP/1.1 417 Expectation Failed < content-type: text/plain < content-length: 20 < server: ballerina < date: Tue, 20 Dec 2022 17:01:59 +0530 * HTTP error before end of send, stop sending < * Closing connection 0 Unprocessable Entity ================================================ FILE: examples/http-100-continue/http_100_continue.md ================================================ # HTTP service - 100 continue Convenience functions are provided in the `http` module for ease of use when handling `100-continue` scenarios. The `100-continue` indicates that the server has received the request headers and the client can proceed with sending the request. It is done by invoking the `continue` method of the `http:Caller` which results in an interim response containing the `100 Continue` status code if allowed. This is useful when handling multipart or large request payloads. ::: code http_100_continue.bal ::: Run the service as follows. ::: out http_100_continue.server.out ::: Invoke the service by executing the following cURL commands in a new terminal. ::: out http_100_continue.client.out ::: ## Related links - [`expects100Continue()` - API documentation](https://lib.ballerina.io/ballerina/http/latest#Request#expects100Continue) ================================================ FILE: examples/http-100-continue/http_100_continue.metatags ================================================ description: This example is on how HTTP 100-continue can be handled by the Ballerina server and the HTTP client connector. keywords: ballerina, ballerina by example, bbe, http, 100-continue ================================================ FILE: examples/http-100-continue/http_100_continue.server.out ================================================ $ bal run http_expect_header.bal time = 2021-01-21 20:31:28,347 level = INFO module = "" message = "TEST 100 CONTINUE" ================================================ FILE: examples/http-100-continue/tests/http_100_continue_test.bal ================================================ import ballerina/http; import ballerina/test; @test:Config {} function testFunc() returns error? { // Invoking the main function http:Client httpEndpoint = check new("localhost:9090"); string response1 = "Hello World!\n"; // Send a GET request to the specified endpoint string response = check httpEndpoint->post("/greeting", "Hello from client"); test:assertEquals(response, response1); } ================================================ FILE: examples/http-2-0-client-server-push/http_2_0_client_server_push.bal ================================================ import ballerina/http; import ballerina/log; // Create an HTTP client that can send HTTP/2 messages. http:Client clientEP = check new ("localhost:9090"); public function main() returns error? { http:Request serviceReq = new; // Submit a request. http:HttpFuture httpFuture = check clientEP->submit("GET", "/http2Service", serviceReq); http:PushPromise?[] promises = []; int promiseCount = 0; // Check if promises exists. boolean hasPromise = clientEP->hasPromise(httpFuture); while hasPromise { // Get the next promise. http:PushPromise pushPromise = check clientEP->getNextPromise(httpFuture); log:printInfo("Received a promise for " + pushPromise.path); if pushPromise.path == "/resource2" { // The client is not interested in receiving `/resource2`. // Therefore, reject the promise. clientEP->rejectPromise(pushPromise); log:printInfo("Push promise for resource2 rejected"); } else { // Store the required promises. promises[promiseCount] = pushPromise; promiseCount = promiseCount + 1; } hasPromise = clientEP->hasPromise(httpFuture); } // Get the requested resource. http:Response response = check clientEP->getResponse(httpFuture); json responsePayload = check response.getJsonPayload(); log:printInfo("Response : " + responsePayload.toJsonString()); // Fetch required promise responses. foreach var p in promises { http:PushPromise promise = p; http:Response promisedResponse = check clientEP->getPromisedResponse(promise); json promisedPayload = check promisedResponse.getJsonPayload(); log:printInfo("Promised resource : " + promisedPayload.toJsonString()); } } ================================================ FILE: examples/http-2-0-client-server-push/http_2_0_client_server_push.md ================================================ # HTTP client - HTTP/2 Server push HTTP/2 server push messages can be received using the Ballerina `http:Client`. HTTP/2 Server Push messages allow the server to send resources to the client before the client requests them. ::: code http_2_0_client_server_push.bal ::: ## Prerequisites - Run the HTTP service given in the [HTTP/2 server push](/learn/by-example/http-2-0-server-push/) example. Run the client program by executing the following command. ::: out http_2_0_client_server_push.out ::: ## Related links - [`hasPromise()` - API documentation](https://lib.ballerina.io/ballerina/http/latest#Client#hasPromise) - [`getNextPromise()` - API documentation](https://lib.ballerina.io/ballerina/http/latest#Client#getNextPromise) - [`rejectPromise()` - API documentation](https://lib.ballerina.io/ballerina/http/latest#Client#rejectPromise) - [Push promise and promise response - Specification](/spec/http/#1011-push-promise-and-promise-response) ================================================ FILE: examples/http-2-0-client-server-push/http_2_0_client_server_push.metatags ================================================ description: This example demonstrates sending and receiving HTTP/2 server push messages by using the Ballerina HTTP package. keywords: ballerina, ballerina by examples, bbe, http, http/2.0, http/2 ================================================ FILE: examples/http-2-0-client-server-push/http_2_0_client_server_push.out ================================================ $ bal run http_2_0_client_server_push.bal time = 2021-01-21 18:54:45,237 level = INFO module = "" message = "Received a promise for /resource1" time = 2021-01-21 18:54:45,278 level = INFO module = "" message = "Received a promise for /resource2" time = 2021-01-21 18:54:45,281 level = INFO module = "" message = "Push promise for resource2 rejected" time = 2021-01-21 18:54:45,283 level = INFO module = "" message = "Received a promise for /resource3" time = 2021-01-21 18:54:45,306 level = INFO module = "" message = "Response : {"response":{"name":"main resource"}}" time = 2021-01-21 18:54:45,314 level = INFO module = "" message = "Promised resource : {"push":{"name":"resource1"}}" time = 2021-01-21 18:54:45,468 level = INFO module = "" message = "Promised resource : {"push":{"name":"resource3"}}" ================================================ FILE: examples/http-2-0-server-push/http_2_0_server_push.bal ================================================ import ballerina/http; // Create an endpoint with port 9090 to accept HTTP requests. listener http:Listener http2ServiceEP = new (9090); service /http2service on http2ServiceEP { resource function 'default .(http:Caller caller) returns error? { // Send a push promise. http:PushPromise promise1 = new (path = "/resource1", method = "GET"); check caller->promise(promise1); // Send another push promise. http:PushPromise promise2 = new (path = "/resource2", method = "GET"); check caller->promise(promise2); // Send one more push promise. http:PushPromise promise3 = new (path = "/resource3", method = "GET"); check caller->promise(promise3); // Construct the requested resource. http:Response res = new; json msg = {"response": {"name": "main resource"}}; res.setPayload(msg); // Send the requested resource. check caller->respond(res); // Construct promised resource1. http:Response push1 = new; msg = {"push": {"name": "resource1"}}; push1.setPayload(msg); // Push promised `resource1`. check caller->pushPromisedResponse(promise1, push1); // Construct promised `resource2`. http:Response push2 = new; msg = {"push": {"name": "resource2"}}; push2.setPayload(msg); // Push promised `resource2`. check caller->pushPromisedResponse(promise2, push2); // Construct promised `resource3`. http:Response push3 = new; msg = {"push": {"name": "resource3"}}; push3.setPayload(msg); // Push promised `resource3`. check caller->pushPromisedResponse(promise3, push3); } } ================================================ FILE: examples/http-2-0-server-push/http_2_0_server_push.md ================================================ # HTTP service - HTTP/2 Server push HTTP/2 server push messages can be sent using the Ballerina `http` service. HTTP/2 server push messages allow the server to send resources to the client before the client requests them. ::: code http_2_0_server_push.bal ::: Run the service by executing the following command. ::: out http_2_0_server_push.out ::: >**Tip:** You can invoke the above service via the [Server push client](/learn/by-example/http-2-0-client-server-push/) example. ## Related links - [`promise()` - API documentation](https://lib.ballerina.io/ballerina/http/latest#Caller#promise) - [`pushPromisedResponse()` - API documentation](https://lib.ballerina.io/ballerina/http/latest#Caller#pushPromisedResponse) - [HTTP service Server push - Specification](/spec/http/#1011-push-promise-and-promise-response) ================================================ FILE: examples/http-2-0-server-push/http_2_0_server_push.metatags ================================================ description: This example demonstrates sending and receiving HTTP/2 server push messages by using the Ballerina HTTP package. keywords: ballerina, ballerina by examples, bbe, http, http/2.0, http/2 ================================================ FILE: examples/http-2-0-server-push/http_2_0_server_push.out ================================================ $ bal run http_2_0_server_push.bal ================================================ FILE: examples/http-2-prior-knowledge-client/http_2_prior_knowledge_client.bal ================================================ import ballerina/http; import ballerina/io; type Album readonly & record { string title; string artist; }; public function main() returns error? { // Enable HTTP/2 prior knowledge. http:Client albumClient = check new ("localhost:9090", http2Settings = { http2PriorKnowledge: true } ); Album[] albums = check albumClient->/albums; io:println("GET request:" + albums.toJsonString()); } ================================================ FILE: examples/http-2-prior-knowledge-client/http_2_prior_knowledge_client.md ================================================ # HTTP client - HTTP/2 prior knowledge The HTTP client is configured to enable HTTP/2 prior knowledge. So the client will directly send requests using HTTP/2 without an HTTP/2 upgrade request. ::: code http_2_prior_knowledge_client.bal ::: ## Prerequisites - Run the HTTP service given in the [Basic REST service](/learn/by-example/http-basic-rest-service/) example. Run the client program by executing the following command. ::: out http_2_prior_knowledge_client.out ::: ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [http client - Specification](https://ballerina.io/spec/http/#24-client) ================================================ FILE: examples/http-2-prior-knowledge-client/http_2_prior_knowledge_client.metatags ================================================ description: This example shows how the Ballerina HTTP client can be configured to enable HTTP/2 prior knowledge. keywords: ballerina, ballerina by examples, bbe, http, http/2, prior knowledge ================================================ FILE: examples/http-2-prior-knowledge-client/http_2_prior_knowledge_client.out ================================================ $ bal run http_2_prior_knowledge_client.bal GET request:[{"title":"Blue Train", "artist":"John Coltrane"}, {"title":"Jeru", "artist":"Gerry Mulligan"}] ================================================ FILE: examples/http-2-to-1-1-downgrade-client/http_2_to_1_1_downgrade_client.bal ================================================ import ballerina/http; import ballerina/io; type Album readonly & record { string title; string artist; }; public function main() returns error? { // Since the default HTTP version is 2.0, HTTP version is set to 1.1. http:Client albumClient = check new ("localhost:9090", httpVersion = http:HTTP_1_1); Album[] albums = check albumClient->/albums; io:println("GET request:" + albums.toJsonString()); } ================================================ FILE: examples/http-2-to-1-1-downgrade-client/http_2_to_1_1_downgrade_client.md ================================================ # HTTP client - HTTP/2 to HTTP/1.1 downgrade The HTTP client is configured to run over the HTTP/1.1 protocol. This client only sends requests over the HTTP/1.1 protocol. When you send a request to an HTTP2-supported service using this client, the connection gets downgraded to HTTP/1.1. If the client is configured to communicate over HTTPS, the ALPN negotiation of choosing which protocol to be used over the secure connection is handled internally. This avoids additional round trips and is independent of the application-layer protocols. ::: code http_2_to_1_1_downgrade_client.bal ::: ## Prerequisites - Run the HTTP service given in the [Basic REST service](/learn/by-example/http-basic-rest-service/) example. Run the client program by executing the following command. ::: out http_2_to_1_1_downgrade_client.out ::: ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [http client - Specification](https://ballerina.io/spec/http/#24-client) ================================================ FILE: examples/http-2-to-1-1-downgrade-client/http_2_to_1_1_downgrade_client.metatags ================================================ description: This example shows how the Ballerina HTTP client can be configured to communicate over the HTTP/1.1 protocol. keywords: ballerina, ballerina by examples, bbe, http, http/1.1, http/2 ================================================ FILE: examples/http-2-to-1-1-downgrade-client/http_2_to_1_1_downgrade_client.out ================================================ $ bal run http_2_to_1_1_downgrade_client.bal GET request:[{"title":"Blue Train", "artist":"John Coltrane"}, {"title":"Jeru", "artist":"Gerry Mulligan"}] ================================================ FILE: examples/http-2-to-1-1-downgrade-service/http_2_to_1_1_downgrade_service.bal ================================================ import ballerina/http; type Album readonly & record {| string title; string artist; |}; table key(title) albums = table [ {title: "Blue Train", artist: "John Coltrane"}, {title: "Jeru", artist: "Gerry Mulligan"} ]; // Since the default HTTP version is 2.0, HTTP version is set to 1.1. service / on new http:Listener(9090, httpVersion = http:HTTP_1_1) { resource function get albums() returns Album[] { return albums.toArray(); } resource function post albums(Album album) returns Album { albums.add(album); return album; } } ================================================ FILE: examples/http-2-to-1-1-downgrade-service/http_2_to_1_1_downgrade_service.client.out ================================================ $ curl http://localhost:9090/albums [{"title":"Blue Train", "artist":"John Coltrane"}, {"title":"Jeru", "artist":"Gerry Mulligan"}] ================================================ FILE: examples/http-2-to-1-1-downgrade-service/http_2_to_1_1_downgrade_service.md ================================================ # HTTP service - HTTP/2 to HTTP/1.1 downgrade The HTTP service is configured to run over the HTTP/1.1 protocol. Therefore this service only accepts requests received over the HTTP/1.1 protocol. If an HTTP2-enabled client sends a request to this service, the client gets downgraded to use HTTP/1.1. If the listener is configured to communicate over HTTPS, the ALPN negotiation of choosing which protocol to be used over the secure connection is handled internally. This avoids additional round trips and is independent of the application-layer protocols. ::: code http_2_to_1_1_downgrade_service.bal ::: Run the service as follows. ::: out http_2_to_1_1_downgrade_service.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out http_2_to_1_1_downgrade_service.client.out ::: >**Tip:** You can invoke the above service via the [Send request/Receive response client](/learn/by-example/http-client-send-request-receive-response/) example. ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [HTTP resource - Specification](https://ballerina.io/spec/http/#23-resource) ================================================ FILE: examples/http-2-to-1-1-downgrade-service/http_2_to_1_1_downgrade_service.metatags ================================================ description: This example shows how a Ballerina HTTP service can be configured to accept messages only over the HTTP/1.1 protocol. keywords: ballerina, ballerina by examples, bbe, http, http/1.1, http/2 ================================================ FILE: examples/http-2-to-1-1-downgrade-service/http_2_to_1_1_downgrade_service.server.out ================================================ $ bal run http_2_to_1_1_downgrade_service.bal ================================================ FILE: examples/http-2-to-1-1-downgrade-service/tests/http_2_0_to_1_1_downgrade_service_test.bal ================================================ import ballerina/test; import ballerina/http; @test:Config {} function testFunc() returns error? { http:Client httpEndpoint = check new ("localhost:9090", httpVersion = http:HTTP_1_1); json[] payload = check httpEndpoint->get("/albums"); test:assertEquals(payload, [{title: "Blue Train", artist: "John Coltrane"}, {title: "Jeru", artist: "Gerry Mulligan"}]); } ================================================ FILE: examples/http-access-logs/http_access_logs.bal ================================================ import ballerina/http; public type Album readonly & record {| string title; string artist; |}; table key(title) albums = table [ {title: "Blue Train", artist: "John Coltrane"}, {title: "Jeru", artist: "Gerry Mulligan"} ]; service / on new http:Listener(9090) { resource function get albums() returns Album[] { return albums.toArray(); } } ================================================ FILE: examples/http-access-logs/http_access_logs.client.out ================================================ $ curl http://localhost:9090/albums [{"title":"Blue Train", "artist":"John Coltrane"}, {"title":"Jeru", "artist":"Gerry Mulligan"}] ================================================ FILE: examples/http-access-logs/http_access_logs.md ================================================ # HTTP service - Access logs Ballerina allows enabling HTTP access logs to record the HTTP requests handled by an application. HTTP access logs are disabled by default. To enable them, set `console=true` under `ballerina.http.accessLogConfig` in the `Config.toml` file. In addition to logging to the console, logs can be written to a file using the `file` configuration. This configuration allows specifying the log file location and related settings, including log rotation. The log format can be specified as either `flat` or `json` using the optional `format` field (defaults to `flat`). Furthermore, you can customize the logged attributes using the optional `attributes` field. ::: code http_access_logs.bal ::: ## Prerequisites - Populate the `Config.toml` file with the access log configurations. ::: code Config.toml ::: Run the service as follows. ::: out http_access_logs.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out http_access_logs.client.out ::: >**Tip:** You can invoke the above service via the [Send request/Receive response client](/learn/by-example/http-client-send-request-receive-response/) example. ## Related links - [`http:AccessLogConfiguration` record - API documentation](https://lib.ballerina.io/ballerina/http/latest#AccessLogConfiguration) - [HTTP service access log - Specification](/spec/http/#824-access-log) ================================================ FILE: examples/http-access-logs/http_access_logs.metatags ================================================ description: This example demonstrates the access logs for HTTP services in Ballerina. keywords: ballerina, ballerina by example, bbe, http, access log, service ================================================ FILE: examples/http-access-logs/http_access_logs.server.out ================================================ $ bal run http_access_logs.bal ballerina: HTTP access log enabled 127.0.0.1 [11/Jul/2024:13:21:01.620 +0530] "GET /albums HTTP/1.1" 200 95 "-" "curl/8.4.0" ================================================ FILE: examples/http-basic-rest-service/http_basic_rest_service.bal ================================================ import ballerina/http; type Album readonly & record {| string title; string artist; |}; table key(title) albums = table [ {title: "Blue Train", artist: "John Coltrane"}, {title: "Jeru", artist: "Gerry Mulligan"} ]; service / on new http:Listener(9090) { resource function get albums() returns Album[] { return albums.toArray(); } resource function post albums(Album album) returns Album { albums.add(album); return album; } } ================================================ FILE: examples/http-basic-rest-service/http_basic_rest_service.client.1.out ================================================ $ curl http://localhost:9090/albums [{"title":"Blue Train", "artist":"John Coltrane"}, {"title":"Jeru", "artist":"Gerry Mulligan"}] ================================================ FILE: examples/http-basic-rest-service/http_basic_rest_service.client.2.out ================================================ $ curl http://localhost:9090/albums -H "Content-type:application/json" -d "{\"title\": \"Sarah Vaughan and Clifford Brown\", \"artist\": \"Sarah Vaughan\"}" {"title":"Sarah Vaughan and Clifford Brown", "artist":"Sarah Vaughan"} ================================================ FILE: examples/http-basic-rest-service/http_basic_rest_service.md ================================================ # REST service - Basic Ballerina language has first-class abstractions for service and resource concepts in the form of `service` and `resource methods`. A resource method consists of an accessor and path. A service can have a collection of resource methods. These abstractions allow mapping REST concepts such as operations, resource paths and resource representations cleanly into your program. `http:Service` can be used to write RESTful services. A service is defined with a base path, the path common to all resource paths. Each resource method is defined with the required operation such as `get`, `put`, `post`, etc and the path. Similar to regular functions resource methods have input parameters and return types that are mapped to the HTTP request and response. ::: code http_basic_rest_service.bal ::: Run the service as follows. ::: out http_basic_rest_service.server.out ::: Invoke the HTTP GET resource by executing the following cURL command in a new terminal. ::: out http_basic_rest_service.client.1.out ::: Invoke the HTTP POST resource by executing the following cURL command in a new terminal. ::: out http_basic_rest_service.client.2.out ::: >**Tip:** You can invoke the above service via the [Send request/Receive response client](/learn/by-example/http-client-send-request-receive-response/) example. ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [HTTP service resource - Specification](/spec/http/#23-resource) ================================================ FILE: examples/http-basic-rest-service/http_basic_rest_service.metatags ================================================ description: This example demonstrates creating a basic REST service in Ballerina. keywords: ballerina, ballerina by example, bbe, http service, path, verb, rest ================================================ FILE: examples/http-basic-rest-service/http_basic_rest_service.server.out ================================================ $ bal run http_basic_rest_service.bal ================================================ FILE: examples/http-basic-rest-service/tests/http_basic_rest_service_test.bal ================================================ import ballerina/test; import ballerina/http; @test:Config {} function testFunc() returns error? { http:Client httpEndpoint = check new("localhost:9090"); Album[] payload = check httpEndpoint->get("/albums"); test:assertEquals(payload, [{title:"Blue Train",artist:"John Coltrane"},{title:"Jeru",artist:"Gerry Mulligan"}]); Album lastAlbum = check httpEndpoint->post("/albums", {title:"Sarah Vaughan and Clifford Brown", artist:"Sarah Vaughan"}); test:assertEquals(lastAlbum, {title:"Sarah Vaughan and Clifford Brown", artist:"Sarah Vaughan"}); } ================================================ FILE: examples/http-caching-client/http_caching_client.bal ================================================ import ballerina/http; import ballerina/io; type Album readonly & record { string title; string artist; }; public function main() returns error? { // In this example, the `isShared` field of the `cacheConfig` is set // to true, as the cache will be a public cache in this particular scenario. http:Client albumClient = check new ("localhost:9090", cache = { isShared: true } ); Album album = check albumClient->/albums/Jeru; io:println("Received album: " + album.toJsonString()); } ================================================ FILE: examples/http-caching-client/http_caching_client.md ================================================ # HTTP client - Enable Caching HTTP caching is enabled by default in the `http:Client`. The cache configurations can be set by the `cache` field in the `http:ClientConfiguration`. The default behavior is to allow caching only when the `Cache-Control` header and either the `ETag` or `Last-Modified` headers are present. Use the `policy` field of the `http:CacheConfig` if you want to change this default behavior. The `http` module currently supports the following policies: `http:CACHE_CONTROL_AND_VALIDATORS` (the default policy) and `http:RFC_7234`. ::: code http_caching_client.bal ::: ## Prerequisites - Run the HTTP service given in the [Sending cache response service](/learn/by-example/http-service-cache-response/) example. Run the client program by executing the following command. ::: out http_caching_client.out ::: >**Tip:** You can enable the [trace logs](/learn/by-example/http-trace-logs/) for both service and client to observe the in and out traffic. ## Related links - [`http:CacheConfig` record - API documentation](https://lib.ballerina.io/ballerina/http/latest#CacheConfig) - [HTTP client caching - Specification](/spec/http/#2412-caching) ================================================ FILE: examples/http-caching-client/http_caching_client.metatags ================================================ description: This example is on how HTTP caching is configured and performed in Ballerina. keywords: ballerina, ballerina by example, bbe, http, client, caching ================================================ FILE: examples/http-caching-client/http_caching_client.out ================================================ $ bal run http_caching_client.bal Received album: {"title":"Jeru", "artist":"Gerry Mulligan"} ================================================ FILE: examples/http-caching-client/tests/http_caching_client_test.bal ================================================ import ballerina/test; import ballerina/http; @test:Config {} function testFunc() returns error? { http:Client httpClient = check new ("localhost:9090", cache = { enabled: true, isShared: true } ); http:Response response = check httpClient->get("/greeting"); test:assertEquals(response.getHeader("etag"), "ec4ac3d0"); test:assertEquals(response.getHeader("cache-control"), "must-revalidate,public,max-age=15"); test:assertFalse(response.hasHeader("age")); test:assertEquals(response.getTextPayload(), "Hello, World!"); response = check httpClient->get("/greeting"); test:assertEquals(response.getHeader("etag"), "ec4ac3d0"); test:assertEquals(response.getHeader("cache-control"), "must-revalidate,public,max-age=15"); test:assertTrue(response.hasHeader("age")); test:assertEquals(response.getTextPayload(), "Hello, World!"); } service / on new http:Listener(9090) { resource function get greeting() returns http:Response { http:Response res = new; // The `http:ResponseCacheControl` object in the `http:Response` object can be // used for setting the cache control directives associated with the response. // In this example, `max-age` directive is set to 15 seconds indicating that the response // will be fresh for 15 seconds. The `must-revalidate` directive instructs that // the cache should not serve a stale response without validating it with the origin server // first. The `public` directive is set by setting `isPrivate=false`. This indicates that // the response can be cached even by intermediary caches which serve multiple users. http:ResponseCacheControl resCC = new; resCC.maxAge = 15; resCC.mustRevalidate = true; resCC.isPrivate = false; res.cacheControl = resCC; string payload = "Hello, World!"; // The `setETag()` function can be used for generating ETags for // `string`, `json`, and `xml` types. This uses the `getCRC32()` // function from the `ballerina/crypto` module for generating the ETag. res.setETag(payload); // The `setLastModified()` function sets the current time as the `last-modified` header. res.setLastModified(); res.setPayload(payload); // When sending the response, if the `cacheControl` field of the response is set, // and the user has not already set a `cache-control` header, a `cache-control` header // will be set using the directives set in the `cacheControl` object. return res; } } ================================================ FILE: examples/http-caller/http_caller.bal ================================================ import ballerina/http; type Album readonly & record {| string title; string artist; |}; table key(title) albums = table [ {title: "Blue Train", artist: "John Coltrane"}, {title: "Jeru", artist: "Gerry Mulligan"} ]; service / on new http:Listener(9090) { // Caller is defined in the signature parameter. resource function get albums(http:Caller caller) returns error? { http:Response response = new; response.setPayload(albums.toArray()); response.setHeader("x-music-genre", "Jazz"); // Sending the response using the caller functions. check caller->respond(response); } } ================================================ FILE: examples/http-caller/http_caller.client.out ================================================ $ curl -v localhost:9090/albums > GET /albums HTTP/1.1 > Host: localhost:9090 > User-Agent: curl/7.64.1 > Accept: */* > < HTTP/1.1 200 OK < content-type: application/json < x-music-genre: Jazz < content-length: 92 < server: ballerina < date: Tue, 10 Jan 2023 15:52:25 +0530 < [{"title":"Blue Train","artist":"John Coltrane"},{"title":"Jeru","artist":"Gerry Mulligan"}] ================================================ FILE: examples/http-caller/http_caller.md ================================================ # HTTP service - Caller object The `http:Caller` represents the endpoint that initiated the call toward a service. It is used to send responses back to the caller. In addition, it also contains meta information such as remote/local addresses. When the `http:Caller` is defined, the resource method return type is constrained to `error?`. `http:Caller` is useful to handle scenarios such as sending status code `100 Continue` or doing some work after sending the response to the caller. In most cases, `http:Caller` is not required as returning from the resource method sends the response back to the caller. ::: code http_caller.bal ::: Run the service as follows. ::: out http_caller.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out http_caller.client.out ::: >**Tip:** You can invoke the above service via the [Send request/Receive response client](/learn/by-example/http-client-send-request-receive-response/) example. ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [HTTP caller - Specification](/spec/http/#2341-httpcaller) ================================================ FILE: examples/http-caller/http_caller.metatags ================================================ description: This example demonstrates the HTTP caller and its usage with an HTTP service in Ballerina. keywords: ballerina, ballerina by example, bbe, http caller, service ================================================ FILE: examples/http-caller/http_caller.server.out ================================================ $ bal run http_caller.bal ================================================ FILE: examples/http-caller/tests/http_caller_test.bal ================================================ import ballerina/http; import ballerina/test; @test:Config {} function testFunc() returns error? { http:Client albumClient = check new ("localhost:9090"); http:Response response = check albumClient->/albums(); test:assertEquals(response.getHeader("x-music-genre"), "Jazz"); } ================================================ FILE: examples/http-circuit-breaker/http_circuit_breaker.bal ================================================ import ballerina/http; import ballerina/io; type Album readonly & record { string title; string artist; }; public function main() returns error? { http:Client albumClient = check new ("localhost:9090", circuitBreaker = { // The failure calculation window measures how long the circuit breaker keeps the // statistics for the operations. rollingWindow: { // The period is in seconds for which the failure threshold is calculated. timeWindow: 10, // The granularity (in seconds) at which the time window slides. // The rolling window is divided into buckets and slides by these increments. bucketSize: 2, // The minimum number of requests in the rolling window that trips the circuit. requestVolumeThreshold: 0 }, // The threshold for request failures. When this threshold exceeds, the circuit trips. // This is the ratio between failures and total requests. The ratio is calculated using // the requests received within the given rolling window. failureThreshold: 0.2, // The period (in seconds) to wait before attempting to make another request to the upstream service. resetTime: 10, // HTTP response status codes that are considered as failures statusCodes: [400, 404, 500] } ); Album[] payload = check albumClient->/albums; io:println(payload); } ================================================ FILE: examples/http-circuit-breaker/http_circuit_breaker.md ================================================ # HTTP client - Circuit breaker The circuit breaker is used to gracefully handle errors that could occur due to network and backend failures. This is configured in the `circuitBreaker` field of the client configuration. The circuit breaker looks for errors across a rolling time window. After the circuit is broken, it does not send requests to the backend until the `resetTime`. ::: code http_circuit_breaker.bal ::: ## Prerequisites - Run the HTTP service given in the [Basic REST service](/learn/by-example/http-basic-rest-service/) example. Run the program by executing the following command. ::: out http_circuit_breaker.out ::: ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [HTTP client circuit breaker - Specification](/spec/http/#2415-circuit-breaker) ================================================ FILE: examples/http-circuit-breaker/http_circuit_breaker.metatags ================================================ description: This example is on how to use an HTTP circuit breaker to handle requests when a backend is failing. keywords: ballerina, ballerina by examples, bbe, http, resiliency, circuit breaker, circuit break ================================================ FILE: examples/http-circuit-breaker/http_circuit_breaker.out ================================================ $ bal run http_circuit_breaker.bal ================================================ FILE: examples/http-circuit-breaker/tests/http_circuit_breaker_test.bal ================================================ import ballerina/test; import ballerina/http; @test:Config {} function testFunc() returns error? { http:Client httpClient = check new ("localhost:8080", // Configuration options that control the behavior of the circuit // breaker. circuitBreaker = { // Failure calculation window. This is how long the circuit // breaker keeps the statistics for the operations. rollingWindow: { // Time period in seconds for which the failure // threshold is calculated. timeWindow: 10, // The granularity (in seconds) at which the time // window slides. The `RollingWindow` is divided into // buckets and slides by these increments. bucketSize: 2, // Minimum number of requests in the `RollingWindow` that // will trip the circuit. requestVolumeThreshold: 0 }, // The threshold for request failures. // When this threshold exceeds, the circuit trips. This is the // ratio between failures and total requests. The ratio is // calculated using the requests received within the given // rolling window. failureThreshold: 0.2, // The time period (in seconds) to wait before attempting to // make another request to the upstream service. resetTime: 10, // HTTP response status codes that are considered as failures statusCodes: [400, 404, 500] }, timeout = 2 ); string responseString = "Hello World!!!"; // Send 1st GET request to the specified endpoint. string payload = check httpClient->/greeting; test:assertEquals(payload, responseString); // Send 2nd GET request to the specified endpoint payload = check httpClient->/greeting; test:assertEquals(payload, responseString); // Send 3rd GET request to the specified endpoint string|error result = httpClient->/greeting; if result is http:RemoteServerError { test:assertEquals(result.detail().body, "Error occurred while processing the request."); } // Send 4th GET request to the specified endpoint result = httpClient->/greeting; test:assertTrue(result is error, "Invalid type"); // Send 5th GET request to the specified endpoint result = httpClient->/greeting; test:assertTrue(result is error, "Invalid type"); // Send 6th GET request to the specified endpoint result = httpClient->/greeting; test:assertTrue(result is error , "Invalid type"); } service / on new http:Listener(8080) { private int counter = 1; resource function get greeting() returns string|http:InternalServerError { if self.counter % 5 == 3 { self.counter += 1; return {body:"Error occurred while processing the request."}; } else { self.counter += 1; return "Hello World!!!"; } } } ================================================ FILE: examples/http-client-basic-authentication/http_client_basic_authentication.bal ================================================ import ballerina/http; import ballerina/io; type Album readonly & record { string title; string artist; }; public function main() returns error? { // Defines the HTTP client to call the APIs secured with basic authentication. http:Client albumClient = check new ("localhost:9090", auth = { username: "ldclakmal", password: "ldclakmal@123" }, secureSocket = { cert: "../resource/path/to/public.crt" } ); Album[] payload = check albumClient->/albums; io:println(payload); } ================================================ FILE: examples/http-client-basic-authentication/http_client_basic_authentication.md ================================================ # HTTP client - Basic authentication The `http:Client` can connect to a service that is secured with basic authentication by adding the `Authorization: Basic ` header to each request. The username and password for basic authentication can be specified in the `auth` field of the client configuration. ::: code http_client_basic_authentication.bal ::: ## Prerequisites - Run the HTTP service given in the [Basic authentication file user store service](/learn/by-example/http-service-basic-authentication-file-user-store) example. Run the client program by executing the command below. ::: out http_client_basic_authentication.out ::: ## Related links - [`http:CredentialsConfig` - API documentation](https://lib.ballerina.io/ballerina/http/latest#CredentialsConfig) - [`auth` module - API documentation](https://lib.ballerina.io/ballerina/auth/latest/) - [HTTP client basic authentication - Specification](/spec/http/#9115-client---basic-auth) ================================================ FILE: examples/http-client-basic-authentication/http_client_basic_authentication.metatags ================================================ description: This example is on how to secure an HTTP client with Basic Auth in Ballerina. keywords: ballerina, ballerina by example, bbe, http, auth, basic auth ================================================ FILE: examples/http-client-basic-authentication/http_client_basic_authentication.out ================================================ $ bal run http_client_basic_auth.bal [{"title":"Blue Train","artist":"John Coltrane"},{"title":"Jeru","artist":"Gerry Mulligan"}] ================================================ FILE: examples/http-client-bearer-token-authentication/http_client_bearer_token_authentication.bal ================================================ import ballerina/http; import ballerina/io; type Album readonly & record { string title; string artist; }; public function main() returns error? { // Defines the HTTP client to call the APIs secured with bearer token authentication. http:Client albumClient = check new ("localhost:9090", auth = { token: "56ede317-4511-44b4-8579-a08f094ee8c5" }, secureSocket = { cert: "../resource/path/to/public.crt" } ); Album[] payload = check albumClient->/albums; io:println(payload); } ================================================ FILE: examples/http-client-bearer-token-authentication/http_client_bearer_token_authentication.md ================================================ # HTTP client - Bearer token authentication The `http:Client` can connect to a service that is secured with bearer token authentication by adding the `Authorization: Bearer ` header to each request. The bearer token can be specified in the `auth` field of the client configuration. ::: code http_client_bearer_token_authentication.bal ::: ## Prerequisites - Run a sample secured service with bearer token authentication. Run the client program by executing the command below. ::: out http_client_bearer_token_authentication.out ::: ## Related links - [`http:BearerTokenConfig` - API documentation](https://lib.ballerina.io/ballerina/http/latest#BearerTokenConfig) - [`auth` module - API documentation](https://lib.ballerina.io/ballerina/auth/latest/) - [HTTP client bearer token authentication - Specification](/spec/http/#9116-client---bearer-token-auth) ================================================ FILE: examples/http-client-bearer-token-authentication/http_client_bearer_token_authentication.metatags ================================================ description: This example is on how to secure an HTTP client with Bearer token auth in Ballerina. keywords: ballerina, ballerina by example, bbe, http, auth, jwt auth ================================================ FILE: examples/http-client-bearer-token-authentication/http_client_bearer_token_authentication.out ================================================ $ bal run http_client_bearer_token_auth.bal [{"title":"Blue Train","artist":"John Coltrane"},{"title":"Jeru","artist":"Gerry Mulligan"}] ================================================ FILE: examples/http-client-chunking/http_client_chunking.bal ================================================ import ballerina/http; import ballerina/io; type Album readonly & record { string title; string artist; }; public function main() returns error? { http:Client albumClient = check new ("localhost:9090", httpVersion = http:HTTP_1_1, http1Settings = { chunking: http:CHUNKING_NEVER } ); Album payload = check albumClient->/albums.post({ title: "Sarah Vaughan and Clifford Brown", artist: "Sarah Vaughan" }); io:println(payload); } ================================================ FILE: examples/http-client-chunking/http_client_chunking.md ================================================ # HTTP client - Chunking The HTTP client can be configured for chunking. By default, the HTTP client sends messages with the `content-length` header. If the message size is larger than the buffer size (8K), messages are chunked. Chunking can be disabled using the client configuration. The chunking behavior can be configured as `CHUNKING_AUTO`, `CHUNKING_ALWAYS`, or `CHUNKING_NEVER` only available HTTP/1.1 protocol. When the config is set to `CHUNKING_ALWAYS`, chunking happens irrespective of the response payload size. ::: code http_client_chunking.bal ::: ## Prerequisites - Run the HTTP service given in the [Basic REST service](/learn/by-example/http-basic-rest-service/) example. Run the client program by executing the following command. ::: out http_client_chunking.out ::: ## Related links - [`http1Settings` - API documentation](https://lib.ballerina.io/ballerina/http/latest#ClientHttp1Settings) - [HTTP client - Specification](https://ballerina.io/spec/http/#24-client) ================================================ FILE: examples/http-client-chunking/http_client_chunking.metatags ================================================ description: This example is on how to configure the chunking behavior of the HTTP client in Ballerina. keywords: ballerina, ballerina by example, bbe, http, chunked, transfer-encoding ================================================ FILE: examples/http-client-chunking/http_client_chunking.out ================================================ $ bal run http_client_chunking.bal ================================================ FILE: examples/http-client-chunking/tests/http_client_chunking_test.bal ================================================ import ballerina/http; import ballerina/test; @test:Config {} function testFunc() returns error? { http:Client clientEndpoint = check new ("localhost:9090", httpVersion = http:HTTP_1_1, http1Settings = {chunking: http:CHUNKING_NEVER}); string response = check clientEndpoint->/payload.post({"name": "Ballerina"}); test:assertEquals(response, "Outbound request content length: 20"); } service / on new http:Listener(9090) { resource function post payload(@http:Header {name: "Content-length"} string cLen) returns string { //Set the response with the content length. return string `Outbound request content length: ${cLen}`; } } ================================================ FILE: examples/http-client-data-binding/http_client_data_binding.bal ================================================ import ballerina/http; import ballerina/io; type Album readonly & record { string title; string artist; }; public function main() returns error? { // Creates a new client with the Basic REST service URL. http:Client albumClient = check new ("localhost:9090"); // Binding the payload to a `record` array type. // The contextually expected type is inferred from the LHS variable type. Album[] albums = check albumClient->/albums; io:println("First artist name: " + albums[0].artist); } ================================================ FILE: examples/http-client-data-binding/http_client_data_binding.md ================================================ # HTTP client - Payload data binding The `http:Client` payload data-binding allows directly binding the response payload to a given subtype of `anydata`. It does this by mapping a given HTTP content-type to one or more Ballerina types. For instance, `text/plain` is mapped to `string`, whereas `application/json` is mapped to `json`, `record`, etc. The client data-binding can be used by simply assigning the resource method’s returned value to the declared variable. If the response is anything other than 2xx, an `error` is returned and no data-binding is performed. If there is no mapping between the given Ballerina type and the response content-type, again an `error` is returned. Use this when the application is only interested in the response payload but not the headers. When the response payload is JSON, the `record` type is preferred to the `json` type as it provides compile-time validations, better readability, and improved tooling support. ::: code http_client_data_binding.bal ::: ## Prerequisites - Run the HTTP service given in the [Basic REST service](/learn/by-example/http-basic-rest-service/) example. Run the client program by executing the following command. ::: out http_client_data_binding.out ::: ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [HTTP client return types - Specification](/spec/http/#243-client-action-return-types) ================================================ FILE: examples/http-client-data-binding/http_client_data_binding.metatags ================================================ description: This example demonstrates the data-binding capability of an HTTP client. keywords: ballerina, ballerina by example, bbe, http, client, data binding ================================================ FILE: examples/http-client-data-binding/http_client_data_binding.out ================================================ $ bal run http_client_data_binding.bal First artist name: John Coltrane ================================================ FILE: examples/http-client-file-upload/http_client_file_upload.bal ================================================ import ballerina/http; import ballerina/io; import ballerina/mime; public function main() returns error? { http:Client httpClient = check new ("localhost:9090"); http:Request request = new; // Sets the file as the request payload. request.setFileAsPayload("./files/BallerinaLang.pdf", contentType = mime:APPLICATION_PDF); //Sends the request to the receiver service with the file content. string content = check httpClient->/receiver.post(request); // forward the received payload to the caller. io:println(content); } ================================================ FILE: examples/http-client-file-upload/http_client_file_upload.md ================================================ # HTTP client - File upload The output streaming is generally handled through the Ballerina `stream` type. Additionally, the `setFileAsPayload` of the `http:Request` is the support function dedicated to file uploads. This is useful when handling continuous payload, file uploads, etc. ::: code http_client_file_upload.bal ::: ## Prerequisites - Run the HTTP service given in the [Service file upload](/learn/by-example/http-service-file-upload/) example. Run the client program by executing the following command. ::: out http_client_file_upload.out ::: ## Related links - [`setFileAsPayload()` - API documentation](https://lib.ballerina.io/ballerina/http/latest#Request#setFileAsPayload) - [`http` module - Specification](/spec/http/#2423-resource-methods) ================================================ FILE: examples/http-client-file-upload/http_client_file_upload.metatags ================================================ description: This example demonstrates the file upload capability through Ballerina streams. keywords: ballerina, ballerina by example, bbe, http, streaming, client, file upload ================================================ FILE: examples/http-client-file-upload/http_client_file_upload.out ================================================ $ bal run http_client_file_upload.bal File Received! ================================================ FILE: examples/http-client-header-parameter/http_client_header_parameter.bal ================================================ import ballerina/http; import ballerina/io; import ballerina/mime; type Album readonly & record { string title; string artist; }; public function main() returns error? { http:Client albumClient = check new ("localhost:9090"); Album[] albums = check albumClient->/albums({ Accept: mime:APPLICATION_JSON }); io:println("Received albums: " + albums.toJsonString()); } ================================================ FILE: examples/http-client-header-parameter/http_client_header_parameter.md ================================================ # HTTP client - Header parameter The `http:Client` supports sending outbound request headers along with the request payload. These headers can be passed as an argument in the client resource method call. The headers should be provided as a `map`, where the keys represent the header names and the entries represent the header values. The header values can be `string` or `string[]`. Use this when you want to send additional headers as part of the request. ::: code http_client_header_parameter.bal ::: ## Prerequisites - Run the HTTP service given in the [REST service - Header parameter](/learn/by-example/http-header-param/) example. Run the client program by executing the following command. ::: out http_client_header_parameter.out ::: Furthermore, a `post` request with additional headers can be sent as shown below. ::: code http_client_header_parameter_post.bal ::: ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [HTTP client resource methods - Specification](/spec/http/#2423-resource-methods) ================================================ FILE: examples/http-client-header-parameter/http_client_header_parameter.metatags ================================================ description: This example is on how to call a client resource with a header parameter. keywords: ballerina, ballerina by examples, bbe, http, client, header parameter ================================================ FILE: examples/http-client-header-parameter/http_client_header_parameter.out ================================================ $ bal run http_client_header_parameter.bal Received albums: [{"title":"Blue Train", "artist":"John Coltrane"}, {"title":"Jeru", "artist":"Gerry Mulligan"}] ================================================ FILE: examples/http-client-header-parameter/http_client_header_parameter_post.bal ================================================ // Sends a `POST` request to the "/album" resource. // The query parameter can be provided as parameters in the `post` method invocation. string albumId = check albumClient->/album.post({ title: "Blue Train", artist: "John Coltrane" }, // Headers can be specified as a `map` { Accept: mime:APPLICATION_JSON } ); io:println("Added album with id: " + albumId); ================================================ FILE: examples/http-client-mutual-ssl/http_client_mutual_ssl.bal ================================================ import ballerina/http; import ballerina/io; type Album readonly & record { string title; string artist; }; public function main() returns error? { // The HTTP client can be configured to initiate new connections that are secured via mutual SSL. http:Client albumClient = check new ("localhost:9090", secureSocket = { key: { certFile: "../resource/path/to/client-public.crt", keyFile: "../resource/path/to/client-private.key" }, cert: "../resource/path/to/server-public.crt" } ); Album[] payload = check albumClient->/albums; io:println(payload); } ================================================ FILE: examples/http-client-mutual-ssl/http_client_mutual_ssl.md ================================================ # HTTP client - Mutual SSL The `http:Client` allows opening up a connection secured with mutual SSL (mTLS), which is a certificate-based authentication process in which two parties (the client and server) authenticate each other by verifying the digital certificates. It ensures that both parties are assured of each other's identity. The `http:Client` secured with mutual SSL is created by providing the `secureSocket` configurations, which require the client's public certificate as the `certFile`, the client's private key as the `keyFile`, and the server's certificate as the `cert`. Use this to interact with mTLS-encrypted HTTP servers. ::: code http_client_mutual_ssl.bal ::: ## Prerequisites - Run the HTTP service given in the [Mutual SSL service](/learn/by-example/http-service-mutual-ssl/) example. Run the client program by executing the command below. ::: out http_client_mutual_ssl.out ::: ## Related links - [`http:ClientSecureSocket` record - API documentation](https://lib.ballerina.io/ballerina/http/latest#ClientSecureSocket) - [HTTP client mutual SSL - Specification](/spec/http/#924-client---mutual-ssl) ================================================ FILE: examples/http-client-mutual-ssl/http_client_mutual_ssl.metatags ================================================ description: This example is on how to secure an HTTP client with mutual SSL. keywords: ballerina, ballerina by example, bbe, http, mutual ssl, ssl protocols, ciphers ================================================ FILE: examples/http-client-mutual-ssl/http_client_mutual_ssl.out ================================================ $ bal run http_client_mutual_ssl.bal [{"title":"Blue Train","artist":"John Coltrane"},{"title":"Jeru","artist":"Gerry Mulligan"}] ================================================ FILE: examples/http-client-oauth2-client-credentials-grant-type/http_client_oauth2_client_credentials_grant_type.bal ================================================ import ballerina/http; import ballerina/io; type Album readonly & record { string title; string artist; }; public function main() returns error? { // Defines the HTTP client to call the OAuth2 secured APIs. http:Client albumClient = check new ("localhost:9090", auth = { tokenUrl: "https://localhost:9445/oauth2/token", clientId: "FlfJYKBD2c925h4lkycqNZlC2l4a", clientSecret: "PJz0UhTJMrHOo68QQNpvnqAY_3Aa", scopes: "admin", clientConfig: { secureSocket: { cert: "../resource/path/to/public.crt" } } }, secureSocket = { cert: "../resource/path/to/public.crt" } ); Album[] payload = check albumClient->/albums; io:println(payload); } ================================================ FILE: examples/http-client-oauth2-client-credentials-grant-type/http_client_oauth2_client_credentials_grant_type.md ================================================ # HTTP client - OAuth2 client credentials grant type The `http:Client` can connect to a service that is secured with the OAuth2 client credentials grant type by adding the `Authorization: Bearer ` header to each request. The required configurations for this grant type can be specified in the `auth` field of the client configuration. ::: code http_client_oauth2_client_credentials_grant_type.bal ::: ## Prerequisites - Run the HTTP service given in the [OAuth2 service](/learn/by-example/http-service-oauth2/) example. Run the client program by executing the following command. ::: out http_client_oauth2_client_credentials_grant_type.out ::: ## Related links - [`http:OAuth2ClientCredentialsGrantConfig` record - API documentation](https://lib.ballerina.io/ballerina/http/latest#OAuth2ClientCredentialsGrantConfig) - [`oauth2` module - API documentation](https://lib.ballerina.io/ballerina/oauth2/latest/) - [HTTP client OAuth2 client credentials grant type - Specification](/spec/http/#9119-client---grant-types-oauth2) ================================================ FILE: examples/http-client-oauth2-client-credentials-grant-type/http_client_oauth2_client_credentials_grant_type.metatags ================================================ description: This example is on how to secure an HTTP client with OAuth2 client credentials grant type in Ballerina. keywords: ballerina, ballerina by example, bbe, http, auth, oauth2, client credentials grant type ================================================ FILE: examples/http-client-oauth2-client-credentials-grant-type/http_client_oauth2_client_credentials_grant_type.out ================================================ $ bal run http_client_oauth2_client_credentials_grant_type.bal [{"title":"Blue Train","artist":"John Coltrane"},{"title":"Jeru","artist":"Gerry Mulligan"}] ================================================ FILE: examples/http-client-oauth2-jwt-bearer-grant-type/http_client_oauth2_jwt_bearer_grant_type.bal ================================================ import ballerina/http; import ballerina/io; type Album readonly & record { string title; string artist; }; public function main() returns error? { // Defines the HTTP client to call the OAuth2 secured APIs. http:Client albumClient = check new ("localhost:9090", auth = { tokenUrl: "https://localhost:9445/oauth2/token", assertion: "eyJhbGciOiJFUzI1NiIsImtpZCI6Ij[...omitted for brevity...]", clientId: "FlfJYKBD2c925h4lkycqNZlC2l4a", clientSecret: "PJz0UhTJMrHOo68QQNpvnqAY_3Aa", scopes: "admin", clientConfig: { secureSocket: { cert: "../resource/path/to/public.crt" } } }, secureSocket = { cert: "../resource/path/to/public.crt" } ); Album[] payload = check albumClient->/albums; io:println(payload); } ================================================ FILE: examples/http-client-oauth2-jwt-bearer-grant-type/http_client_oauth2_jwt_bearer_grant_type.md ================================================ # HTTP client - OAuth2 JWT bearer grant type The `http:Client` can connect to a service that is secured with the OAuth2 JWT bearer grant type by adding the `Authorization: Bearer ` header to each request. The required configurations for this grant type can be specified in the `auth` field of the client configuration. ::: code http_client_oauth2_jwt_bearer_grant_type.bal ::: ## Prerequisites - Run the HTTP service given in the [OAuth2 service](/learn/by-example/http-service-oauth2/) example. Run the client program by executing the command below. ::: out http_client_oauth2_jwt_bearer_grant_type.out ::: ## Related links - [`http:OAuth2JwtBearerGrantConfig` record - API documentation](https://lib.ballerina.io/ballerina/http/latest#OAuth2JwtBearerGrantConfig) - [`oauth2` module - API documentation](https://lib.ballerina.io/ballerina/oauth2/latest/) - [HTTP client grant types - Specification](/spec/http/#9129-client---grant-types-oauth2) ================================================ FILE: examples/http-client-oauth2-jwt-bearer-grant-type/http_client_oauth2_jwt_bearer_grant_type.metatags ================================================ description: This example is on how to secure an HTTP client with OAuth2 JWT bearer grant type in Ballerina. keywords: ballerina, ballerina by example, bbe, http, auth, oauth2, jwt bearer grant type ================================================ FILE: examples/http-client-oauth2-jwt-bearer-grant-type/http_client_oauth2_jwt_bearer_grant_type.out ================================================ $ bal run http_client_oauth2_jwt_bearer_grant_type.bal [{"title":"Blue Train","artist":"John Coltrane"},{"title":"Jeru","artist":"Gerry Mulligan"}] ================================================ FILE: examples/http-client-oauth2-password-grant-type/http_client_oauth2_password_grant_type.bal ================================================ import ballerina/http; import ballerina/oauth2; import ballerina/io; type Album readonly & record { string title; string artist; }; public function main() returns error? { // Defines the HTTP client to call the OAuth2 secured APIs. http:Client albumClient = check new ("localhost:9090", auth = { tokenUrl: "https://localhost:9445/oauth2/token", username: "admin", password: "admin", clientId: "FlfJYKBD2c925h4lkycqNZlC2l4a", clientSecret: "PJz0UhTJMrHOo68QQNpvnqAY_3Aa", scopes: "admin", refreshConfig: oauth2:INFER_REFRESH_CONFIG, clientConfig: { secureSocket: { cert: "../resource/path/to/public.crt" } } }, secureSocket = { cert: "../resource/path/to/public.crt" } ); Album[] payload = check albumClient->/albums; io:println(payload); } ================================================ FILE: examples/http-client-oauth2-password-grant-type/http_client_oauth2_password_grant_type.md ================================================ # HTTP client - OAuth2 password grant type The `http:Client` can connect to a service that is secured with the OAuth2 password grant type by adding the `Authorization: Bearer ` header to each request. The required configurations for this grant type can be specified in the `auth` field of the client configuration. Use this grant type when you need to exchange the user's credentials for an access token. ::: code http_client_oauth2_password_grant_type.bal ::: ## Prerequisites - Run the HTTP service given in the [OAuth2 service](/learn/by-example/http-service-oauth2/) example. Run the client program by executing the command below. ::: out http_client_oauth2_password_grant_type.out ::: ## Related links - [`http:OAuth2PasswordGrantConfig` - API documentation](https://lib.ballerina.io/ballerina/http/latest#OAuth2PasswordGrantConfig) - [`oauth2` module - API documentation](https://lib.ballerina.io/ballerina/oauth2/latest/) - [HTTP client grant types - Specification](/spec/http/#9129-client---grant-types-oauth2) ================================================ FILE: examples/http-client-oauth2-password-grant-type/http_client_oauth2_password_grant_type.metatags ================================================ description: This example is on how to secure an HTTP client with the OAuth2 password grant type in Ballerina. keywords: ballerina, ballerina by example, bbe, http, auth, oauth2, password grant type ================================================ FILE: examples/http-client-oauth2-password-grant-type/http_client_oauth2_password_grant_type.out ================================================ $ bal run http_client_oauth2_password_grant_type.bal [{"title":"Blue Train","artist":"John Coltrane"},{"title":"Jeru","artist":"Gerry Mulligan"}] ================================================ FILE: examples/http-client-oauth2-refresh-token-grant-type/http_client_oauth2_refresh_token_grant_type.bal ================================================ import ballerina/http; import ballerina/io; type Album readonly & record { string title; string artist; }; public function main() returns error? { // Defines the HTTP client to call the OAuth2 secured APIs. http:Client albumClient = check new ("localhost:9090", auth = { refreshUrl: "https://localhost:9445/oauth2/token", refreshToken: "24f19603-8565-4b5f-a036-88a945e1f272", clientId: "FlfJYKBD2c925h4lkycqNZlC2l4a", clientSecret: "PJz0UhTJMrHOo68QQNpvnqAY_3Aa", scopes: "admin", clientConfig: { secureSocket: { cert: "../resource/path/to/public.crt" } } }, secureSocket = { cert: "../resource/path/to/public.crt" } ); Album[] payload = check albumClient->/albums; io:println(payload); } ================================================ FILE: examples/http-client-oauth2-refresh-token-grant-type/http_client_oauth2_refresh_token_grant_type.md ================================================ # HTTP client - OAuth2 refresh token grant type The `http:Client` can connect to a service that is secured with the OAuth2 refresh token grant type by adding the `Authorization: Bearer ` header to each request. The required configurations for this grant type can be specified in the `auth` field of the client configuration. Use this to automatically retrieve an access token when it is expired. ::: code http_client_oauth2_refresh_token_grant_type.bal ::: ## Prerequisites - Run the HTTP service given in the [OAuth2 service](/learn/by-example/http-service-oauth2/) example. Run the client program by executing the command below. ::: out http_client_oauth2_refresh_token_grant_type.out ::: ## Related links - [`http:OAuth2RefreshTokenGrantConfig` - API documentation](https://lib.ballerina.io/ballerina/http/latest#OAuth2RefreshTokenGrantConfig) - [`oauth2` module - API documentation](https://lib.ballerina.io/ballerina/oauth2/latest/) - [HTTP client grant types - Specification](/spec/http/#9129-client---grant-types-oauth2) ================================================ FILE: examples/http-client-oauth2-refresh-token-grant-type/http_client_oauth2_refresh_token_grant_type.metatags ================================================ description: This example is on how to secure an HTTP client with the OAuth2 refresh token grant type in Ballerina. keywords: ballerina, ballerina by example, bbe, http, auth, oauth2, refresh token grant type ================================================ FILE: examples/http-client-oauth2-refresh-token-grant-type/http_client_oauth2_refresh_token_grant_type.out ================================================ $ bal run http_client_oauth2_refresh_token_grant_type.bal [{"title":"Blue Train","artist":"John Coltrane"},{"title":"Jeru","artist":"Gerry Mulligan"}] ================================================ FILE: examples/http-client-path-parameter/http_client_path_parameter.bal ================================================ import ballerina/http; import ballerina/io; type Album readonly & record { string title; string artist; }; public function main(string artist) returns error? { http:Client albumClient = check new ("localhost:9090"); // Sends a `GET` request to the "/albums" resource. // The path parameter can be defined inside `[]` after the path followed by a `/`. // This is the same as invoking the get method by `albumClient->get("/albums/Blue Train")`. Album album = check albumClient->/albums/[artist]; io:println("Received album: " + album.toJsonString()); } ================================================ FILE: examples/http-client-path-parameter/http_client_path_parameter.md ================================================ # HTTP client - Path parameter The `http:Client` supports specifying `path parameters` when invoking a backend resource. Declare the required `path parameter` inside a square bracket along with the resource path (eg: `/albums/[album]`) and invoke the resource method. Use this when invoking backend resources with variable resource paths. ::: code http_client_path_parameter.bal ::: ## Prerequisites - Run the HTTP service given in the [REST service - Path parameter](/learn/by-example/http-path-param/) example. Run the client program by executing the following command. ::: out http_client_path_parameter.out ::: ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [HTTP client resource methods - Specification](/spec/http/#2423-resource-methods) ================================================ FILE: examples/http-client-path-parameter/http_client_path_parameter.metatags ================================================ description: This example is on how to call a client resource with a path parameter. keywords: ballerina, ballerina by examples, bbe, http, client, path parameter ================================================ FILE: examples/http-client-path-parameter/http_client_path_parameter.out ================================================ $ bal run http_client_path_parameter.bal -- "Blue Train" Received album: {"title":"Blue Train", "artist":"John Coltrane"} ================================================ FILE: examples/http-client-payload-constraint-validation/http_client_payload_constraint_validation.bal ================================================ import ballerina/http; import ballerina/constraint; import ballerina/io; type Album record { // Add a constraint for the maximum length and the minimum length. @constraint:String { maxLength: 5, minLength: 1 } string title; string artist; }; public function main() returns error? { http:Client albumClient = check new ("localhost:9090"); // Request the server for the album. If the constraint validation fails, // an `http:PayloadValidationError` will be returned. Album album = check albumClient->/albums.post({ // Here, an album which exceeds the constraints are sent to a server // which returns the same record again to the client. title: "Blue Train", artist: "John Coltrane" }); io:println("Received album: " + album.toJsonString()); } ================================================ FILE: examples/http-client-payload-constraint-validation/http_client_payload_constraint_validation.md ================================================ # HTTP client - Payload constraint validation The Ballerina `constraint` module allows adding additional constraints to the response payload. The `http:Client` uses the `constraint` module to validate the payload against the given constraints. This validation happens soon after the successful data-binding of the response payload. The constraints can be added to a given data type using different annotations. If the validation fails, an `http:PayloadValidationError` is returned with the validation error message. Use this to validate the response payload as the application program receives it, which protects the client against unnecessary processing and malicious payloads. ::: code http_client_payload_constraint_validation.bal ::: ## Prerequisites - Run the HTTP service given in the [REST service - Payload data binding](/learn/by-example/http-service-data-binding/) example. Run the client program by executing the following command. ::: out http_client_payload_constraint_validation.out ::: ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [`http` module - Specification](/spec/http/) - [Constraint validation example](/learn/by-example/constraint-validations/) ================================================ FILE: examples/http-client-payload-constraint-validation/http_client_payload_constraint_validation.metatags ================================================ description: This example demonstrates the constraint validation capability of an HTTP client. keywords: ballerina, ballerina by example, bbe, http, client, data binding, constraint, validation ================================================ FILE: examples/http-client-payload-constraint-validation/http_client_payload_constraint_validation.out ================================================ $ bal run http_client_payload_constraint_validation.bal error: payload validation failed: Validation failed for '$.title:maxLength' constraint(s). ================================================ FILE: examples/http-client-query-parameter/http_client_query_parameter.bal ================================================ import ballerina/http; import ballerina/io; type Album readonly & record { string title; string artist; }; public function main() returns error? { http:Client albumClient = check new ("localhost:9090"); // Sends a `GET` request to the "/albums" resource. // The query parameter can be provided as parameters in the `get` method invocation. Album[] albums = check albumClient->/albums(artist = "John Coltrane"); io:println("Received albums: " + albums.toJsonString()); } ================================================ FILE: examples/http-client-query-parameter/http_client_query_parameter.md ================================================ # HTTP client - Query parameter The `http:Client` supports specifying query parameters as resource method arguments. Each query parameter can be stated as a key-value pair. When the request is sent, the key-value pairs are appended to the request path (e.g., `?foo=bar`). The supported types are `string`, `int`, `float`, `boolean`, `decimal`, and `array` types of the aforementioned types. Use this when invoking endpoints that expect query parameters. ::: code http_client_query_parameter.bal ::: ## Prerequisites - Run the HTTP service given in the [REST service - Query parameter](/learn/by-example/http-query-parameter/) example. Run the client program by executing the following command. ::: out http_client_query_parameter.out ::: Furthermore, a `post` request with a query parameter can be sent as shown below. ::: code http_client_query_parameter_post.bal ::: ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [HTTP client resource methods - Specification](/spec/http/#2423-resource-methods) ================================================ FILE: examples/http-client-query-parameter/http_client_query_parameter.metatags ================================================ description: This example is on how to call a client resource with a query parameter. keywords: ballerina, ballerina by examples, bbe, http, client, query parameter ================================================ FILE: examples/http-client-query-parameter/http_client_query_parameter.out ================================================ $ bal run http_client_query_parameter.bal Received albums: [{"title":"Blue Train", "artist":"John Coltrane"}] ================================================ FILE: examples/http-client-query-parameter/http_client_query_parameter_post.bal ================================================ // Sends a `POST` request to the "/album" resource. // The query parameter can be provided as parameters in the `post` method invocation. string albumId = check albumClient->/album.post({ title: "Sarah Vaughan and Clifford Brown", artist: "Sarah Vaughan" }, apiVersion = 2 ); io:println("Added album with id: " + albumId); ================================================ FILE: examples/http-client-redirects/http_client_redirects.bal ================================================ import ballerina/http; import ballerina/io; type Album readonly & record {| string title; string artist; |}; public function main() returns error? { // The `followRedirects` record provides configurations associated with HTTP redirects. http:Client albumClient = check new ("localhost:9092", followRedirects = { enabled: true, maxCount: 5 } ); Album[] payload = check albumClient->/redirect; io:println(string `Response received: ${payload.toJsonString()}`); } ================================================ FILE: examples/http-client-redirects/http_client_redirects.md ================================================ # HTTP client - Redirects The `http:Client` supports redirection. If the `http:Response` contains a `redirect` status code with `Location` header, the client internally calls the respective endpoint and returns the successful response. To enable redirection, set `followRedirect` to `true` in the client config. ::: code http_client_redirects.bal ::: ## Prerequisites - Run the HTTP service given in the [Redirect service](/learn/by-example/http-service-redirects/) example. Run the client program by executing the following command. ::: out http_client_redirects.out ::: ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [HTTP client redirect - Specification](/spec/http/#2413-redirect) ================================================ FILE: examples/http-client-redirects/http_client_redirects.metatags ================================================ description: This example is on how to do an HTTP redirect with the Ballerina HTTP client connector. keywords: ballerina, ballerina by example, bbe, http, redirect ================================================ FILE: examples/http-client-redirects/http_client_redirects.out ================================================ $ bal run http_client_redirects.bal Response received: [{"title":"Blue Train", "artist":"John Coltrane"}, {"title":"Jeru", "artist":"Gerry Mulligan"}] ================================================ FILE: examples/http-client-redirects/tests/http_client_redirects_test.bal ================================================ import ballerina/test; import ballerina/http; table key(title) albums = table [ {title: "Blue Train", artist: "John Coltrane"}, {title: "Jeru", artist: "Gerry Mulligan"} ]; @test:Config {} function testFunc() returns error? { http:Client httpEndpoint = check new ("localhost:9092", followRedirects = {enabled: true, maxCount: 5}); // Send a GET request to the specified endpoint Album[] response = check httpEndpoint->get("/redirect"); test:assertEquals(response, [{"title":"Blue Train", "artist":"John Coltrane"}, {"title":"Jeru", "artist":"Gerry Mulligan"}]); } service / on new http:Listener(9092) { resource function get redirect() returns http:TemporaryRedirect { // Return a redirect response record with the location header. return {headers: {"Location": "http://localhost:9090/albums"}}; } } service / on new http:Listener(9090) { resource function get albums() returns Album[] { return albums.toArray(); } resource function post albums(Album album) returns Album { albums.add(album); return album; } } ================================================ FILE: examples/http-client-self-signed-jwt-authentication/http_client_self_signed_jwt_authentication.bal ================================================ import ballerina/http; import ballerina/io; type Album readonly & record { string title; string artist; }; public function main() returns error? { // Defines the HTTP client to call the JWT Auth secured APIs. // The client is enriched with the `Authorization: Bearer ` header by // passing the `http:JwtIssuerConfig` for the `auth` configuration of the // client. A self-signed JWT is issued before the request is sent. http:Client albumClient = check new ("localhost:9090", auth = { username: "ballerina", issuer: "wso2", audience: ["ballerina", "ballerina.org", "ballerina.io"], keyId: "5a0b754-895f-4279-8843-b745e11a57e9", jwtId: "JlbmMiOiJBMTI4Q0JDLUhTMjU2In", customClaims: { "scp": "admin" }, expTime: 3600, signatureConfig: { config: { keyFile: "../resource/path/to/private.key" } } }, secureSocket = { cert: "../resource/path/to/public.crt" } ); Album[] payload = check albumClient->/albums; io:println(payload); } ================================================ FILE: examples/http-client-self-signed-jwt-authentication/http_client_self_signed_jwt_authentication.md ================================================ # HTTP client - Self signed JWT authentication The `http:Client` can connect to a service that is secured with self-signed JWT by adding the `Authorization: Bearer ` header by passing the `http:JwtIssuerConfig` to the `auth` configuration of the client. A self-signed JWT is issued before the request is sent. ::: code http_client_self_signed_jwt_authentication.bal ::: ## Prerequisites - Run the HTTP service given in the [JWT Auth service](/learn/by-example/http-service-jwt-authentication/) example. Run the client program by executing the command below. ::: out http_client_self_signed_jwt_authentication.out ::: ## Related links - [`http:JwtIssuerConfig` record - API documentation](https://lib.ballerina.io/ballerina/http/latest#JwtIssuerConfig) - [`jwt` module - API documentation](https://lib.ballerina.io/ballerina/jwt/latest/) - [HTTP client self signed JWT authentication - Specification](/spec/http/#9127-client---self-signed-jwt) ================================================ FILE: examples/http-client-self-signed-jwt-authentication/http_client_self_signed_jwt_authentication.metatags ================================================ description: This example is on how to secure an HTTP client with self-signed JWT Auth in Ballerina. keywords: ballerina, ballerina by example, bbe, http, auth, jwt auth ================================================ FILE: examples/http-client-self-signed-jwt-authentication/http_client_self_signed_jwt_authentication.out ================================================ $ bal run http_client_self_signed_jwt_authentication.bal [{"title":"Blue Train","artist":"John Coltrane"},{"title":"Jeru","artist":"Gerry Mulligan"}] ================================================ FILE: examples/http-client-send-request-receive-response/http_client_send_request_receive_response.bal ================================================ import ballerina/http; import ballerina/io; type Album readonly & record { string title; string artist; }; public function main() returns error? { // Creates a new client with the Basic REST service URL. http:Client albumClient = check new ("localhost:9090"); // Sends a `GET` request to the "/albums" resource. // The verb is not mandatory as it is default to "GET". Album[] albums = check albumClient->/albums; io:println("GET request:" + albums.toJsonString()); // Sends a `POST` request to the "/albums" resource. Album album = check albumClient->/albums.post({ title: "Sarah Vaughan and Clifford Brown", artist: "Sarah Vaughan" }); io:println("\nPOST request:" + album.toJsonString()); } ================================================ FILE: examples/http-client-send-request-receive-response/http_client_send_request_receive_response.md ================================================ # HTTP client - Send request/Receive response The `http:Client` interacts with an HTTP server. The client is instantiated with the service URL and it uses resource methods to send requests and receive responses from the backend service. The standard HTTP methods `get`, `post`, `put`, `patch`, `delete`, `head`, and `options` are available as resource accessors. A resource method invocation is done by providing the `resource path`, relevant `resource accessor`, and required arguments after the `->`. Since HTTP `get` is the default resource method, the accessor is not mandatory when invoking an HTTP `GET` resource. ::: code http_client_send_request_receive_response.bal ::: ## Prerequisites - Run the HTTP service given in the [Basic REST service](/learn/by-example/http-basic-rest-service/) example. Run the client program by executing the following command. ::: out http_client_send_request_receive_response.out ::: ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [HTTP client - Specification](/spec/http/#24-client) ================================================ FILE: examples/http-client-send-request-receive-response/http_client_send_request_receive_response.metatags ================================================ description: This example is on how to interact with an HTTP server using the Ballerina HTTP client connector. keywords: ballerina, ballerina by examples, bbe, http, client ================================================ FILE: examples/http-client-send-request-receive-response/http_client_send_request_receive_response.out ================================================ $ bal run http_client_send_request_receive_response.bal GET request:[{"title":"Blue Train", "artist":"John Coltrane"}, {"title":"Jeru", "artist":"Gerry Mulligan"}] POST request:{"title":"Sarah Vaughan and Clifford Brown", "artist":"Sarah Vaughan"} ================================================ FILE: examples/http-client-send-request-receive-response/tests/http_client_send_request_receive_response_test.bal ================================================ import ballerina/test; (anydata|error)[] outputs = []; int counter = 0; // 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(anydata|error... a) { outputs[counter] = a[0]; counter += 1; } @test:Config {} function testFunc() returns error? { test:when(mock_printLn).call("mockPrint"); json jsonRes1 = { "args": { "test": "123" }, "url": "https://postman-echo.com/get?test=123" }; json jsonRes2 = { "args": {}, "data": "POST: Hello World", "files": {}, "form": {}, "json": null, "url": "https://postman-echo.com/post" }; json jsonRes5 = { "args": {}, "data": "DELETE: Hello World", "files": {}, "form": {}, "json": null, "url": "https://postman-echo.com/delete" }; // Invoking the main function main(); test:assertEquals(outputs[0], "GET request:"); // Remove the headers since the user-agent will be different // from ballerina version to version. map res = > check outputs[1]; var result = res.remove("headers"); test:assertEquals(res, jsonRes1); test:assertEquals(outputs[2], "\nPOST request:"); res = > check outputs[3]; result = res.remove("headers"); test:assertEquals(res, jsonRes2); test:assertEquals(outputs[4], "\nDELETE request:"); res = > check outputs[5]; result = res.remove("headers"); test:assertEquals(res, jsonRes5); test:assertEquals(outputs[6], "\nUse custom HTTP verbs:"); test:assertEquals(outputs[7], "Content-Type: application/json; charset=utf-8"); test:assertEquals(outputs[8], "Status code: 200"); } ================================================ FILE: examples/http-client-ssl-tls/http_client_ssl_tls.bal ================================================ import ballerina/http; import ballerina/io; type Album readonly & record { string title; string artist; }; public function main() returns error? { http:Client albumClient = check new ("localhost:9090", secureSocket = { cert: "../resource/path/to/public.crt" } ); Album[] payload = check albumClient->/albums; io:println(payload); } ================================================ FILE: examples/http-client-ssl-tls/http_client_ssl_tls.md ================================================ # HTTP client - SSL/TLS The `http:Client` can be configured to communicate through HTTPS by providing a certificate file. The certificate can be provided through the `secureSocket` field of the client configuration. Use this to secure the communication between the client and the server. ::: code http_client_ssl_tls.bal ::: ## Prerequisites - Run the HTTP service given in the [SSL/TLS service](/learn/by-example/http-service-ssl-tls/) example. Run the secure client program by executing the command below. ::: out http_client_ssl_tls.out ::: ## Related links - [`http:ClientSecureSocket` record - API documentation](https://lib.ballerina.io/ballerina/http/latest#ClientSecureSocket) - [HTTP client SSL/TLS - Specification](/spec/http/#923-client---ssltls) ================================================ FILE: examples/http-client-ssl-tls/http_client_ssl_tls.metatags ================================================ description: This example is on how to secure an HTTP client with SSL. keywords: ballerina, ballerina by example, bbe, http, ssl, tls ================================================ FILE: examples/http-client-ssl-tls/http_client_ssl_tls.out ================================================ $ bal run http_client_ssl_tls.bal [{"title":"Blue Train","artist":"John Coltrane"},{"title":"Jeru","artist":"Gerry Mulligan"}] ================================================ FILE: examples/http-compression/http_compression.bal ================================================ import ballerina/http; // `COMPRESSION_ALWAYS` guarantees a compressed response entity body. The compression scheme is set to the // value indicated in `Accept-Encoding` request header. When a particular header is not present or the header // value is `identity`, encoding is done using the "gzip" scheme. // By default, Ballerina compresses any MIME type unless they are mentioned under `contentTypes`. // Compression can be constrained to certain MIME types by specifying them as an array of MIME types. // In this example encoding is applied to `text/plain` responses only. @http:ServiceConfig { compression: { enable: http:COMPRESSION_ALWAYS, contentTypes: ["text/plain"] } } service / on new http:Listener(9090) { // The response entity body is always compressed since MIME type has matched. resource function 'default compress() returns string { return "Type : This is a string."; } } ================================================ FILE: examples/http-compression/http_compression.client.out ================================================ $ curl -v --output - http://localhost:9090/compress > GET /compress HTTP/1.1 > Host: localhost:9090 > User-Agent: curl/7.58.0 > Accept: */* > < HTTP/1.1 200 OK < content-type: text/plain < server: ballerina < content-encoding: gzip < content-length: 48 < � * Connection #0 to host localhost left intact �,HU���,V�D�⒢̼t��ʴ�� ================================================ FILE: examples/http-compression/http_compression.md ================================================ # HTTP service - Compression The `http:Service` can be configured to change the compression behaviour of the `http:Response` payload. By default, the server compresses the response entity body with the scheme(gzip, deflate) that is specified in the `Accept-Encoding` request header. When the particular header is not present, or the header value is `identity`, the server does not perform any compression. Compression is disabled when the option is set to `COMPRESSION_NEVER` and always enabled when the option is set to `COMPRESSION_ALWAYS`. In the same way `http:Client` can be configured as well. ::: code http_compression.bal ::: Run the service as follows. ::: out http_compression.server.out ::: Invoke the service by executing the following cURL command in a new terminal. Here, the `Accept-Encoding` header is not specified. ::: out http_compression.client.out ::: ## Related links - [`COMPRESSION_ALWAYS` - API documentation](https://lib.ballerina.io/ballerina/http/latest#COMPRESSION_ALWAYS) - [HTTP service configuration - Specification](https://ballerina.io/spec/http/#241-client-types) ================================================ FILE: examples/http-compression/http_compression.metatags ================================================ description: This example demonstrates how to configure a Ballerina HTTP service to change its compression behavior. keywords: ballerina, ballerina by example, bbe, http, compression, accept-encoding ================================================ FILE: examples/http-compression/http_compression.server.out ================================================ $ bal run http_compression.bal ================================================ FILE: examples/http-compression/tests/http_compression_test.bal ================================================ import ballerina/http; import ballerina/test; @test:Config {} function testFunc() returns error? { http:Client httpEndpoint = check new("localhost:9090"); // Send a GET request to the specified endpoint. string response = check httpEndpoint->get("/compress"); // Assert the uncompressed response. test:assertEquals(response, "Type : This is a string."); } ================================================ FILE: examples/http-cookies-client/http_cookies_client.bal ================================================ import ballerina/http; import ballerina/log; // HTTP client configurations associated with enabling cookies. http:ClientConfiguration clientEPConfig = { cookieConfig: { enabled: true } }; public function main() returns error? { http:Client httpClient = check new ("localhost:9095/cookieDemo", clientEPConfig); // Send an outbound request to the `login` backend resource with username and password. string loginResp = check httpClient->post("/login", { name: "John", password: "p@ssw0rd" }); log:printInfo(loginResp); // Make another request to the `/welcome` resource of the backend service. // As cookies are enabled in the HTTP client, it automatically handles cookies received with the // login response and sends the relevant cookies to the `welcome` service resource. string welcomeResp = check httpClient->get("/welcome"); log:printInfo(welcomeResp); } ================================================ FILE: examples/http-cookies-client/http_cookies_client.md ================================================ # HTTP client - Cookies HTTP cookies can track, personalize, and manage the session between the `http:Client` and service. Cookie client behavior is enabled using the `http:ClientConfiguration`. If the cookie-enabled client receives a response with a cookie, the subsequent requests are sent along with the same cookie. Therefore the same session id is passed back to the service to retrieve the previous state. This is useful to maintain stateful interaction. ::: code http_cookies_client.bal ::: ## Prerequisites - Run the HTTP service given in the [Cookies service](/learn/by-example/http-cookies-service/) example. Run the client program by executing the following command. ::: out http_cookies_client.out ::: ## Related links - [`http:Cookie` - API documentation](https://lib.ballerina.io/ballerina/http/latest#Cookie) - [HTTP service cookie - Specification](/spec/http/#2416-cookie) ================================================ FILE: examples/http-cookies-client/http_cookies_client.metatags ================================================ description: This example is on how to handle HTTP cookies in a Ballerina client. keywords: ballerina, ballerina by example, bbe, http, cookies, client ================================================ FILE: examples/http-cookies-client/http_cookies_client.out ================================================ $ bal run http_client.bal time = 2022-12-05T18:29:26.378+05:30 level = INFO module = "" message = "Login succeeded" time = 2022-12-05T18:29:26.441+05:30 level = INFO module = "" message = "Welcome back John" ================================================ FILE: examples/http-cookies-service/http_cookies_service.bal ================================================ import ballerina/http; listener http:Listener serverEP = new (9095); service /cookieDemo on serverEP { resource function post login(map details) returns http:Response|http:Unauthorized|error { // Retrieve the username and password. json name = check details.name; json password = check details.password; // Check the password value. if password == "p@ssw0rd" { // Create a new cookie by setting `name` as the `username` and `value` as the logged-in user's name. // Set the cookies path as `/` to apply it to all the resources in the service. http:Cookie cookie = new ("username", name.toString(), path = "/"); http:Response response = new; // Add the created cookie to the response. response.addCookie(cookie); // Set a message payload to inform that the login has // been succeeded. response.setTextPayload("Login succeeded"); return response; } return http:UNAUTHORIZED; } resource function get welcome(http:Request req) returns string|http:NotFound { // Retrieve cookies from the request. http:Cookie[] cookies = req.getCookies(); // Get the cookie value of the `username`. http:Cookie[] usernameCookie = cookies.filter(function(http:Cookie cookie) returns boolean { return cookie.name == "username"; }); if usernameCookie.length() > 0 { string? user = usernameCookie[0].value; if user is string { // Respond with the username added to the welcome message. return "Welcome back " + user; } } return http:NOT_FOUND; } } ================================================ FILE: examples/http-cookies-service/http_cookies_service.md ================================================ # HTTP service - Cookies HTTP cookies can track, personalize, and manage the session in the service. The cookies can be accessed from the `getCookies` method of the `http:Request`. Setting cookies back in the response is done by the `addCookie` method of the `http:Response`. This is useful for services to maintain the state. ::: code http_cookies_service.bal ::: Run the service as follows. ::: out http_cookies_service.out ::: >**Tip:** You can invoke the above service via the [Cookies client](/learn/by-example/http-cookies-client/) example. ## Related links - [`http:Cookie` - API documentation](https://lib.ballerina.io/ballerina/http/latest#Cookie) - [HTTP service cookie - Specification](/spec/http/#2416-cookie) ================================================ FILE: examples/http-cookies-service/http_cookies_service.metatags ================================================ description: This example is on how to handle HTTP cookies in a Ballerina service. keywords: ballerina, ballerina by example, bbe, http, cookies, server ================================================ FILE: examples/http-cookies-service/http_cookies_service.out ================================================ $ bal run cookie_server.bal ================================================ FILE: examples/http-cors/http_cors.bal ================================================ import ballerina/http; // The service-level CORS config applies globally to each `resource`. @http:ServiceConfig { cors: { allowOrigins: ["http://www.m3.com", "http://www.hello.com"], allowCredentials: false, allowHeaders: ["CORELATION_ID"], exposeHeaders: ["X-CUSTOM-HEADER"], maxAge: 84900 } } service /crossOriginService on new http:Listener(9092) { // The resource-level CORS config overrides the service-level CORS headers. @http:ResourceConfig { cors: { allowOrigins: ["http://www.bbc.com"], allowCredentials: true, allowHeaders: ["X-Content-Type-Options", "X-PINGOTHER"] } } resource function get company() returns string { return "middleware"; } // Since there are no resource-level CORS configs defined here, the global // service-level CORS configs will be applied to this resource. resource function post lang(@http:Payload string lang) returns string { return lang; } } ================================================ FILE: examples/http-cors/http_cors.client.out ================================================ # To send a CORS simple request. $ curl -v "http://localhost:9092/crossOriginService/company" -H "Origin:http://www.bbc.com" > GET /crossOriginService/company HTTP/1.1 > Host: localhost:9092 > User-Agent: curl/7.64.1 > Accept: */* > Origin:http://www.bbc.com > < HTTP/1.1 200 OK < content-type: text/plain < access-control-allow-origin: http://www.bbc.com < access-control-allow-credentials: true < content-length: 10 < server: ballerina < date: Sat, 22 Oct 2022 20:51:49 +0530 < * Connection #0 to host localhost left intact middleware* Closing connection 0 # To send a CORS preflight request. $ curl -v "http://localhost:9092/crossOriginService/lang" -X OPTIONS -H "Origin:http://www.m3.com" -H "Access-Control-Request-Method:POST" > OPTIONS /crossOriginService/lang HTTP/1.1 > Host: localhost:9092 > User-Agent: curl/7.64.1 > Accept: */* > Origin:http://www.m3.com > Access-Control-Request-Method:POST > < HTTP/1.1 204 No Content < access-control-allow-origin: http://www.m3.com < access-control-max-age: 84900 < access-control-allow-methods: POST < server: ballerina < date: Sat, 22 Oct 2022 20:53:12 +0530 < * Connection #0 to host localhost left intact * Closing connection 0 ================================================ FILE: examples/http-cors/http_cors.md ================================================ # HTTP service - CORS (Cross-Origin Resource Sharing) Cross-Origin Resource Sharing (CORS) is an HTTP-header-based mechanism that allows a server to indicate any origins other than its own from which a browser should permit. The CORS headers can be applied at both the service level and the resource level. Each configuration has the `cors` field to specify the CORS config. Service-level CORS headers apply to all the resources unless there are headers configured at the resource level. Ballerina CORS supports both simple and pre-flight requests. ::: code http_cors.bal ::: Run the service as follows. ::: out http_cors.server.out ::: Invoke the service by executing the following cURL commands in a new terminal. ::: out http_cors.client.out ::: ## Related links - [`http:CorsConfig` record - API documentation](https://lib.ballerina.io/ballerina/http/latest#CorsConfig) - [HTTP service configuration - Specification](/spec/http/#41-service-configuration); ================================================ FILE: examples/http-cors/http_cors.metatags ================================================ description: This example demonstrates the HTTP service-level and resource-level CORS configurations. keywords: ballerina, ballerina by example, bbe, http, cors ================================================ FILE: examples/http-cors/http_cors.server.out ================================================ $ bal run http_cors.bal ================================================ FILE: examples/http-cors/tests/http_cors_test.bal ================================================ import ballerina/test; import ballerina/http; @test:Config {} function testFunc() returns error? { http:Client httpEndpoint = check new("localhost:9092"); string expected = "middleware"; // Send a `GET` request to the specified endpoint. string companyResponse = check httpEndpoint->get("/crossOriginService/company", {"Origin":"http://www.bbc.com"}); test:assertEquals(companyResponse, expected); var headers = {"Origin": "http://www.m3.com", "Access-Control-Request-Method": "POST"}; // Send a `GET` request to the specified endpoint. http:Response|error langResponse = httpEndpoint->options("/crossOriginService/lang", headers); if langResponse is http:Response { // Asserting the header values. test:assertEquals(langResponse.getHeader("Access-Control-Allow-Methods"), "POST"); test:assertEquals(langResponse.getHeader("Access-Control-Allow-Origin"), "http://www.m3.com"); } else { test:assertFail(msg = "Failed to call the endpoint:"); } } ================================================ FILE: examples/http-default-error-handling/http_default_error_handling.bal ================================================ import ballerina/http; type Album readonly & record { string title; string artist; }; service / on new http:Listener(9090) { resource function get artist() returns string|error { // Creates a new client with an invalid endpoint URL. http:Client albumClient = check new ("localhost:9091"); Album[] albums = check albumClient->/albums; return "First artist name: " + albums[0].artist; } } ================================================ FILE: examples/http-default-error-handling/http_default_error_handling.client.out ================================================ $ curl -v localhost:9090/artist * Trying 127.0.0.1:9090... * TCP_NODELAY set * Connected to localhost (127.0.0.1) port 9090 (#0) > GET /artist HTTP/1.1 > Host: localhost:9090 > User-Agent: curl/7.68.0 > Accept: */* > * Mark bundle as not supporting multiuse < HTTP/1.1 502 Bad Gateway < content-type: application/json < content-length: 210 < server: ballerina < date: Fri, 16 Jun 2023 10:40:52 +0530 < * Connection #0 to host localhost left intact {"timestamp":"2023-06-16T05:10:52.220224Z", "status":502, "reason":"Bad Gateway", "message":"Something wrong with the connection: Connection refused: localhost/127.0.0.1:9091", "path":"/artist", "method":"GET"} ================================================ FILE: examples/http-default-error-handling/http_default_error_handling.md ================================================ # HTTP service - Error handling The Ballerina `http` module allows returning `error`s from the resource method. Therefore, application logic written inside a resource method could propagate errors from the resource method to the `http:Listener`. Though there are many ways to propagate errors, the most common way is to use the `check` keyword. `http:Listener` intercepts these errors and sends a `500 Internal Server Error` response with the error message in the payload. In addition, it logs the error with the stack trace in the console. Users can take control of this behavior by adding a `do/fail` block in the resource method and handling the errors themselves. When there is a repetition of error handling logic in the resource method, the repetition can be avoided by moving the logic to an Error handler interceptor. >**Note:** Errors originating from the `http:Listener` itself due to resource not found, data-binding, authorization, etc are converted into respective status codes such as `500 Internal Server Error`, `400 Bad Request`, `401 Unauthorized`, etc. Again, users can take control of this behavior by adding an Error handler interceptor. ::: code http_default_error_handling.bal ::: Run the service as follows. ::: out http_default_error_handling.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out http_default_error_handling.client.out ::: ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [HTTP error handling - Specification](/spec/http/#82-error-handling) ================================================ FILE: examples/http-default-error-handling/http_default_error_handling.metatags ================================================ description: This example demonstrates how HTTP error handling is done by default in Ballerina. keywords: ballerina, ballerina by example, bbe, http, interceptor, error ================================================ FILE: examples/http-default-error-handling/http_default_error_handling.server.out ================================================ $ bal run http_default_error_handling.bal error: Something wrong with the connection cause: Connection refused: localhost/127.0.0.1:9091 ================================================ FILE: examples/http-default-error-handling/tests/http_default_error_handling_test.bal ================================================ import ballerina/http; import ballerina/test; @test:Config {} function testFunc() returns error? { http:Client httpEndpoint = check new("localhost:9090"); http:Response response = check httpEndpoint->get("/artist"); test:assertEquals(response.statusCode, 502); } ================================================ FILE: examples/http-default-resource/http_default_resource.bal ================================================ import ballerina/http; service on new http:Listener(9090) { // The `default` accessor can be used to match with all methods including the standard HTTP methods // and custom methods. The rest param is used to represent the wildcard of the `resource path` in which any path // segment will get dispatched to the resource in the absence of an exact path match. resource function 'default [string... paths](http:Request req) returns string { return string `method: ${req.method}, path: ${paths.toString()}`; } } ================================================ FILE: examples/http-default-resource/http_default_resource.client.out ================================================ $ curl "http://localhost:9090/foo/bar" method: GET, path: ["foo","bar"] ================================================ FILE: examples/http-default-resource/http_default_resource.md ================================================ # HTTP service - Default resource The default resource slightly varies from the usual resource method as it uses `rest parameters` as the `resource path` and the `default` identifier as the `resource accessor`. The `rest parameters` allow any of the URL paths to be matched, and it supports `string`, `int`, `float`, `boolean`, and `decimal` as types. The `default` identifier also allows any HTTP methods to be dispatched to the resource method. Use it when designing a REST API to handle proxy services or as a default location to get dispatched if none of the other resources are matched. ::: code http_default_resource.bal ::: Run the service as follows. ::: out http_default_resource.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out http_default_resource.client.out ::: ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [HTTP service default resource - Specification](/spec/http/#233-path-parameter) ================================================ FILE: examples/http-default-resource/http_default_resource.metatags ================================================ description: This example is on how to use the `rest-param` and the `default` accessor (HTTP verb) in Ballerina to dispatch and constrain the service in a RESTful manner. keywords: ballerina, ballerina by example, bbe, http service, path, verb ================================================ FILE: examples/http-default-resource/http_default_resource.server.out ================================================ $ bal run http_default_resource.bal ================================================ FILE: examples/http-default-resource/tests/http_default_resource_test.bal ================================================ import ballerina/http; import ballerina/test; @test:Config {} function testFunc() returns error? { http:Client httpEndpoint = check new("localhost:9090"); string response = check httpEndpoint->get("/foo/bar"); test:assertEquals(response, "method: GET, path: [\"foo\",\"bar\"]"); } ================================================ FILE: examples/http-error-handling/http_error_handling.bal ================================================ import ballerina/http; type Album readonly & record {| string title; string artist; |}; table key(title) albums = table [ {title: "Blue Train", artist: "John Coltrane"}, {title: "Sarah Vaughan and Clifford Brown", artist: "Sarah Vaughan"} ]; // A `ResponseErrorInterceptor` service class implementation. service class ResponseErrorInterceptor { *http:ResponseErrorInterceptor; // The error occurred in the request-response path can be accessed by the // mandatory argument: `error`. The remote method can return a response, // which will overwrite the existing error response. remote function interceptResponseError(error err) returns http:BadRequest { // In this case, all the errors are sent as `400 BadRequest` responses with a customized // media type and body. Moreover, you can send different status code responses according to // the error type. return { mediaType: "application/org+json", body: {message: err.message()} }; } } // Engage interceptors at the service level using an `http:InterceptableService`. The base path of the // interceptor services is the same as the target service. Hence, they will be executed only for // this particular service. service http:InterceptableService / on new http:Listener(9090) { // Service-level error interceptors can handle errors occurred during the service execution. public function createInterceptors() returns ResponseErrorInterceptor { // To handle all of the errors, the `ResponseErrorInterceptor` is added as a first // interceptor as it has to be executed last. return new ResponseErrorInterceptor(); } // If the request does not have an`x-api-version` header, then an error will be returned // and the execution will jump to the nearest `ResponseErrorInterceptor`. resource function get albums(@http:Header {name: "x-api-version"} string xApiVersion) returns Album[]|http:NotImplemented { if xApiVersion != "v1" { return http:NOT_IMPLEMENTED; } return albums.toArray(); } } ================================================ FILE: examples/http-error-handling/http_error_handling.client.out ================================================ $ curl -v http://localhost:9090/albums * Trying 127.0.0.1:9090... * Connected to localhost (127.0.0.1) port 9090 (#0) > GET /albums HTTP/1.1 > Host: localhost:9090 > User-Agent: curl/7.79.1 > Accept: */* > * Mark bundle as not supporting multiuse < HTTP/1.1 400 Bad Request < content-type: application/org+json < content-length: 55 < server: ballerina < date: Wed, 14 Dec 2022 15:33:27 +0530 < {"message":"no header value found for 'x-api-version'"} ================================================ FILE: examples/http-error-handling/http_error_handling.md ================================================ # HTTP service - Error handling Error handling is an integral part of any network program. Errors can be returned by many components such as an interceptor, dispatcher, data binder, security handler, etc. These errors are often handled by a default handler and sent back as `500 InternalSeverError` responses with the error message in the body. This behavior can be changed by adding error interceptors to the interceptor pipeline, which can intercept these errors and handle them as you wish. These error interceptors can be placed anywhere in the interceptor pipeline. When there is an error, execution jumps to the closest error interceptor. Use these error interceptors to handle errors yourself and create appropriate responses for different error types. ::: code http_error_handling.bal ::: Run the service as follows. ::: out http_error_handling.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out http_error_handling.client.out ::: >**Tip:** You can invoke the above service via the [Send request/Receive response client](/learn/by-example/http-client-send-request-receive-response/) example. ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [HTTP service error handling - Specification](/spec/http/#82-error-handling) ================================================ FILE: examples/http-error-handling/http_error_handling.metatags ================================================ description: This example demonstrates how to handle HTTP errors manually in Ballerina. keywords: ballerina, ballerina by example, bbe, http, interceptor, error ================================================ FILE: examples/http-error-handling/http_error_handling.server.out ================================================ $ bal run http_error_handling.bal ================================================ FILE: examples/http-error-handling/tests/http_error_handling_test.bal ================================================ import ballerina/http; import ballerina/test; @test:Config{} function testFunc() returns error? { json expectedBody = { message : "no header value found for 'x-api-version'" }; http:Client clientEP = check new("localhost:9090"); http:Response res = check clientEP->get("/albums"); test:assertEquals(res.statusCode, 400); test:assertEquals(res.getContentType(), "application/org+json"); test:assertEquals(check res.getJsonPayload(), expectedBody); } ================================================ FILE: examples/http-failover/http_failover.bal ================================================ import ballerina/http; import ballerina/io; type Album readonly & record { string title; string artist; }; public function main() returns error? { // Define the failover client endpoint to call the backend services. http:FailoverClient albumClient = check new ({ timeout: 5, failoverCodes: [501, 502, 503], interval: 5, // Define a set of HTTP Clients that are targeted for failover. targets: [ {url: "http://nonexistentEP"}, {url: "http://localhost:9090"} ] }); Album[] payload = check albumClient->/albums; io:println(payload); } ================================================ FILE: examples/http-failover/http_failover.md ================================================ # HTTP client - Failover A failover client is used to preserve the continuity of the requests flow even if the endpoint fails. The endpoints are defined in the `targets` field of the failover client configuration. If one of the endpoints fails, the client automatically fails over to another endpoint. ::: code http_failover.bal ::: ## Prerequisites - Run the HTTP service given in the [Basic REST service](/learn/by-example/http-basic-rest-service/) example. Run the program by executing the following command. ::: out http_failover.out ::: ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [HTTP client failover - Specification](/spec/http/#2418-failover) ================================================ FILE: examples/http-failover/http_failover.metatags ================================================ description: This example is on how to use an HTTP failover client to handle network-related issues gracefully in Ballerina. keywords: ballerina, ballerina by examples, bbe, http, resiliency, failover ================================================ FILE: examples/http-failover/http_failover.out ================================================ $ bal run http_failover.bal ================================================ FILE: examples/http-failover/tests/http_failover_test.bal ================================================ import ballerina/test; import ballerina/http; import ballerina/lang.runtime; @test:Config {} function testFunc() returns error? { http:FailoverClient httpEndpoint = check new ({ timeout: 5, failoverCodes: [501, 502, 503], interval: 5, targets: [ {url: "http://nonexistentEP/mock1"}, {url: "http://localhost:8080/echo"}, {url: "http://localhost:8080/mock"} ] }); string response = check httpEndpoint->/greeting; test:assertEquals(response, "The mock resource is invoked."); } // Define the sample service to mock connection timeouts and service outages. service / on new http:Listener(8080) { resource function 'default echo/greeting() returns string { // Delay the response for 30 seconds to mimic network level delays. runtime:sleep(30); return "The echo resource is invoked"; } // Define the sample resource to mock a healthy service. resource function 'default mock/greeting() returns string { return "The mock resource is invoked."; } } ================================================ FILE: examples/http-header-param/http_header_param.bal ================================================ import ballerina/http; import ballerina/mime; type Album readonly & record {| string title; string artist; |}; table key(title) albums = table [ {title: "Blue Train", artist: "John Coltrane"}, {title: "Jeru", artist: "Gerry Mulligan"} ]; service / on new http:Listener(9090) { // The `accept` argument with `@http:Header` annotation takes the value of the `Accept` request header. resource function get albums(@http:Header string accept) returns Album[]|http:NotAcceptable { if !string:equalsIgnoreCaseAscii(accept, mime:APPLICATION_JSON) { return http:NOT_ACCEPTABLE; } return albums.toArray(); } } ================================================ FILE: examples/http-header-param/http_header_param.client.out ================================================ $ curl "http://localhost:9090/albums" -H "Accept:application/json" [{"title":"Blue Train", "artist":"John Coltrane"}, {"title":"Jeru", "artist":"Gerry Mulligan"}] ================================================ FILE: examples/http-header-param/http_header_param.md ================================================ # REST service - Header parameter The `@http:header` annotation allows reading header values from the request. The annotation can be used to annotate a given resource parameter. The name of the parameter must match the name of the header. If there is a mismatch, then the header name must be given in the annotation configuration. The resource parameter can be a simple type or an array type (i.e., `string version` or `string[] versions`). If there are many headers to read, a record type can be used as the parameter. Unless the parameter is optional (i.e., `string? version`), a `400 Bad Request` response is sent to the client in the absence of the mapping header. ::: code http_header_param.bal ::: Run the service as follows. ::: out http_header_param.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out http_header_param.client.out ::: >**Tip:** You can invoke the above service via the client given in the [HTTP client - Header parameter](/learn/by-example/http-client-header-parameter/) example. ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [HTTP service header parameter - Specification](/spec/http/#2345-header-parameter) ================================================ FILE: examples/http-header-param/http_header_param.metatags ================================================ description: This example is on how to use the `@http:Header` annotation to retrieve HTTP headers. keywords: ballerina, ballerina by example, bbe, http, http-headers ================================================ FILE: examples/http-header-param/http_header_param.server.out ================================================ $ bal run http_headers.bal ================================================ FILE: examples/http-header-param/tests/http_header_param_test.bal ================================================ import ballerina/http; import ballerina/test; @test:Config {} function testFunc() returns error? { http:Client testClient = check new("localhost:9090"); Album[] payload = check testClient->get("/albums", {"Accept":"application/json"}); test:assertEquals(payload, [{title:"Blue Train",artist:"John Coltrane"},{title:"Jeru",artist:"Gerry Mulligan"}]); Album|error response = testClient->get("/albums", {"Accept":"application/xml"}); if response is http:ClientRequestError { test:assertEquals(response.detail().statusCode, 406); } else { test:assertFail("Unexpected status code"); } } ================================================ FILE: examples/http-interceptor-error-handling/http_interceptor_error_handling.bal ================================================ import ballerina/http; type Album readonly & record {| string title; string artist; |}; table key(title) albums = table [ {title: "Blue Train", artist: "John Coltrane"}, {title: "Sarah Vaughan and Clifford Brown", artist: "Sarah Vaughan"} ]; service class RequestInterceptor { *http:RequestInterceptor; // This will return a `HeaderNotFoundError` if you do not set the `x-api-version` header. // Then, the execution will jump to the nearest `RequestErrorInterceptor`. resource function 'default [string... path]( http:RequestContext ctx, @http:Header {name: "x-api-version"} string xApiVersion) returns http:NotImplemented|http:NextService|error? { // Checks the API version header. if xApiVersion != "v1" { return http:NOT_IMPLEMENTED; } return ctx.next(); } } // A `RequestErrorInterceptor` service class implementation. It allows intercepting // the error that occurred in the request path and handle it accordingly. // A `RequestErrorInterceptor` service class can have only one resource method. service class RequestErrorInterceptor { *http:RequestErrorInterceptor; // The resource method inside a `RequestErrorInterceptor` is only allowed // to have the default method and path. The error occurred in the interceptor // execution can be accessed by the mandatory argument: `error`. resource function 'default [string... path](error err) returns http:BadRequest { // In this case, all of the errors are sent as `400 BadRequest` responses with a customized // media type and body. You can also send different status code responses according to // the error type. Furthermore, you can also call `ctx.next()` if you want to continue the // request flow after fixing the error. return { mediaType: "application/org+json", body: {message: err.message()} }; } } service http:InterceptableService / on new http:Listener(9090) { public function createInterceptors() returns [RequestInterceptor, RequestErrorInterceptor] { // To handle all of the errors in the request path, the `RequestErrorInterceptor` // is added as the last interceptor as it has to be executed last. return [new RequestInterceptor(), new RequestErrorInterceptor()]; } resource function get albums() returns Album[] { return albums.toArray(); } } ================================================ FILE: examples/http-interceptor-error-handling/http_interceptor_error_handling.client.out ================================================ $ curl -v http://localhost:9090/albums * Trying 127.0.0.1:9090... * Connected to localhost (127.0.0.1) port 9090 (#0) > GET /albums HTTP/1.1 > Host: localhost:9090 > User-Agent: curl/7.79.1 > Accept: */* > * Mark bundle as not supporting multiuse < HTTP/1.1 400 Bad Request < content-type: application/org+json < content-length: 55 < server: ballerina < date: Wed, 14 Dec 2022 15:33:27 +0530 < {"message":"no header value found for 'x-api-version'"} ================================================ FILE: examples/http-interceptor-error-handling/http_interceptor_error_handling.md ================================================ # HTTP service - Interceptor error handling Errors that occurred in the request-response flow can be intercepted and handled by the `ResponseErrorInterceptors`. In addition, the `http:RequestErrorInterceptor` can be used to handle errors that occurred while executing the `RequestInterceptors`. This error interceptor can send a response according to the error similar to a `ResponseErrorInterceptor`. Moreover, it can modify the request and dispatch it to the target service. Use `RequestErrorInterceptors` along with `RequestInterceptors` to validate the request beforehand and handle any errors during this validation. ::: code http_interceptor_error_handling.bal ::: Run the service as follows. ::: out http_interceptor_error_handling.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out http_interceptor_error_handling.client.out ::: >**Tip:** You can invoke the above service via the [Send request/Receive response client](/learn/by-example/http-client-send-request-receive-response/) example. ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [HTTP service error handling - Specification](/spec/http/#82-error-handling) ================================================ FILE: examples/http-interceptor-error-handling/http_interceptor_error_handling.metatags ================================================ description: This example demonstrates how HTTP interceptor error handling is done in Ballerina. keywords: ballerina, ballerina by example, bbe, http, interceptor, error, request, filter ================================================ FILE: examples/http-interceptor-error-handling/http_interceptor_error_handling.server.out ================================================ $ bal run http_interceptor_error_handling.bal ================================================ FILE: examples/http-interceptor-error-handling/tests/http_interceptor_error_handling_test.bal ================================================ import ballerina/http; import ballerina/test; @test:Config{} function testFunc() returns error? { json expectedBody = { message : "no header value found for 'x-api-version'" }; http:Client clientEP = check new("localhost:9090"); http:Response res = check clientEP->get("/albums"); test:assertEquals(res.statusCode, 400); test:assertEquals(res.getContentType(), "application/org+json"); test:assertEquals(check res.getJsonPayload(), expectedBody); } ================================================ FILE: examples/http-load-balancer/http_load_balancer.bal ================================================ import ballerina/http; import ballerina/io; type Album readonly & record { string title; string artist; }; public function main() returns error? { // Define the load balance client endpoint to call the backend services. http:LoadBalanceClient albumClient = check new ({ // Define the set of HTTP clients that need to be load balanced. targets: [ {url: "http://localhost:9090"}, {url: "http://localhost:9091"}, {url: "http://localhost:9092"} ], timeout: 5 }); Album[] payload = check albumClient->/albums; io:println(payload); } ================================================ FILE: examples/http-load-balancer/http_load_balancer.md ================================================ # HTTP client - Load balancer A load-balancing client is used when the request load needs to be load balanced across a given set of target endpoints. These endpoints are defined in the `targets` field of the load-balancing client configuration. ::: code http_load_balancer.bal ::: ## Prerequisites - Run multiple instances of the HTTP service given in [Basic REST services](/learn/by-example/http-basic-rest-service/) example by changing the ports accordingly. Run the program by executing the following command. ::: out http_load_balancer.out ::: ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [HTTP service load balance - Specification](/spec/http/#2417-load-balance) ================================================ FILE: examples/http-load-balancer/http_load_balancer.metatags ================================================ description: This example is on how to use an HTTP load balancer in Ballerina to balance the load of an endpoint using a given algorithm. keywords: ballerina, ballerina by examples, bbe, http, resiliency, load balancer, load balance ================================================ FILE: examples/http-load-balancer/http_load_balancer.out ================================================ $ bal run http_load_balancer.bal ================================================ FILE: examples/http-load-balancer/tests/http_load_balancer_test.bal ================================================ import ballerina/test; import ballerina/http; @test:Config {} function testFunc() returns error? { http:LoadBalanceClient httpEndpoint = check new ({ targets: [ {url: "http://localhost:8080/mock1"}, {url: "http://localhost:8080/mock2"}, {url: "http://localhost:8080/mock3"} ], timeout: 5 }); string response = check httpEndpoint->/greeting; test:assertEquals(response, "Mock1 resource was invoked."); response = check httpEndpoint->/greeting; test:assertEquals(response, "Mock2 resource was invoked."); response = check httpEndpoint->/greeting; test:assertEquals(response, "Mock3 resource was invoked."); response = check httpEndpoint->/greeting; test:assertEquals(response, "Mock1 resource was invoked."); } service / on new http:Listener(8080) { resource function get mock1/greeting() returns string { return "Mock1 resource was invoked."; } resource function get mock2/greeting() returns string { return "Mock2 resource was invoked."; } resource function get mock3/greeting() returns string { return "Mock3 resource was invoked."; } } ================================================ FILE: examples/http-matrix-param/http_matrix_param.bal ================================================ import ballerina/http; type Album readonly & record {| string title; string artist; |}; table key(title) albums = table [ {title: "Blue-Train", artist: "John-Coltrane"}, {title: "Jeru", artist: "Gerry-Mulligan"} ]; service / on new http:Listener(9090) { resource function get albums/[string title](http:Request req) returns Album|http:NotFound|http:BadRequest { Album? album = albums[title]; if album is () { return http:NOT_FOUND; } // Gets the `MatrixParams` of the path `/albums`. map pathMParams = req.getMatrixParams("/albums"); string artist = pathMParams["artist"]; if album.artist != artist { return http:BAD_REQUEST; } return album; } } ================================================ FILE: examples/http-matrix-param/http_matrix_param.client.out ================================================ $ curl "http://localhost:9090/albums;artist=John-Coltrane/Blue-Train" {"title":"Blue-Train", "artist":"John-Coltrane"} ================================================ FILE: examples/http-matrix-param/http_matrix_param.md ================================================ # HTTP service - Matrix parameter The matrix parameter enhances the hierarchical structure of HTTP URIs. The `http:Request` has the `getMatrixParams` method to extract the matrix parameter map from the given path segment. ::: code http_matrix_param.bal ::: Run the service as follows. ::: out http_matrix_param.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out http_matrix_param.client.out ::: ## Related links - [`getMatrixParams()` - API documentation](https://lib.ballerina.io/ballerina/http/latest#Request#getMatrixParams) - [HTTP service matrix parameter - Specification](https://ballerina.io/spec/http/#53-matrix) ================================================ FILE: examples/http-matrix-param/http_matrix_param.metatags ================================================ description: This example is on how to extract values using the matrix path parameters in Ballerina. keywords: ballerina, ballerina by example, bbe, http, matrix param ================================================ FILE: examples/http-matrix-param/http_matrix_param.server.out ================================================ $ bal run http_matrix_param.bal ================================================ FILE: examples/http-matrix-param/tests/http_matrix_param_test.bal ================================================ import ballerina/http; import ballerina/test; @test:Config {} function testFunc() returns error? { http:Client testClient = check new("localhost:9090"); json payload = check testClient->get("/albums;artist=John-Coltrane/Blue-Train"); test:assertEquals(payload, {title:"Blue-Train", artist:"John-Coltrane"}); http:Response res = check testClient->get("/albums;artist=John/Blue-Train"); test:assertEquals(res.statusCode, 400); } ================================================ FILE: examples/http-passthrough/http_passthrough.bal ================================================ import ballerina/http; type Album readonly & record { string title; string artist; }; http:Client clientEP = check new ("localhost:9090"); service / on new http:Listener(9092) { resource function 'default [string... path](http:Request req) returns Album[]|error { Album[] payload = check clientEP->forward("/albums", req); return payload; } } ================================================ FILE: examples/http-passthrough/http_passthrough.client.out ================================================ $ curl http://localhost:9092/passthrough [{"title":"Blue Train", "artist":"John Coltrane"}, {"title":"Jeru", "artist":"Gerry Mulligan"}]* ================================================ FILE: examples/http-passthrough/http_passthrough.md ================================================ # HTTP service - Passthrough The passthrough service forwards the inbound request to the backend and returns the backend response. The passthrough resource is designed to allow all HTTP methods as the accessor is the `default`. Also the rest parameter in the resource path as it allows any request URI to get dispatched. When `forward()` is called on the backend client, it forwards the request that the passthrough resource received to the backend. When forwarding, the request is made using the same HTTP method that was used to invoke the passthrough resource. The `forward()` function returns the response from the backend if there are no errors. This is useful to delegate the functionality to the downstream services. ::: code http_passthrough.bal ::: Run the service as follows. ::: out http_passthrough.server.out ::: ## Prerequisites - Run the HTTP service given in the [Basic REST service](/learn/by-example/http-basic-rest-service/) example as the backend service. Invoke the service by executing the following cURL command in a new terminal. ::: out http_passthrough.client.out ::: ## Related links - [`forward()` - API documentation](https://lib.ballerina.io/ballerina/http/latest#Client#forward) - [HTTP service forward method - Specification](/spec/http/#2424-forwardexecute-methods) ================================================ FILE: examples/http-passthrough/http_passthrough.metatags ================================================ description: This example demonstrates the process of an HTTP passthrough service in Ballerina. keywords: ballerina, ballerina by example, bbe, http, passthrough, client ================================================ FILE: examples/http-passthrough/http_passthrough.server.out ================================================ $ bal run passthrough.bal ================================================ FILE: examples/http-passthrough/tests/http_passthrough_test.bal ================================================ import ballerina/http; import ballerina/test; @test:Config {} function testFunc() returns error? { http:Client httpEndpoint = check new ("localhost:9092"); http:Response response = check httpEndpoint->get("/passthrough"); test:assertEquals(response.statusCode, 200); } table key(title) albums = table [ {title: "Blue Train", artist: "John Coltrane"}, {title: "Jeru", artist: "Gerry Mulligan"} ]; service / on new http:Listener(9090) { resource function get albums() returns Album[] { return albums.toArray(); } resource function post albums(Album album) returns Album { albums.add(album); return album; } } ================================================ FILE: examples/http-path-param/http_path_param.bal ================================================ import ballerina/http; type Album readonly & record {| string title; string artist; |}; table key(title) albums = table [ {title: "Blue Train", artist: "John Coltrane"}, {title: "Jeru", artist: "Gerry Mulligan"} ]; service / on new http:Listener(9090) { // The path param is defined as a part of the resource path within brackets // along with the type and it is extracted from the request URI. resource function get albums/[string title]() returns Album|http:NotFound { Album? album = albums[title]; if album is () { return http:NOT_FOUND; } return album; } } ================================================ FILE: examples/http-path-param/http_path_param.client.out ================================================ $ curl "http://localhost:9090/albums/Jeru" {"title":"Jeru", "artist":"Gerry Mulligan"} ================================================ FILE: examples/http-path-param/http_path_param.md ================================================ # REST service - Path parameter The `path parameter` is a mandatory but variable part of a resource URL. `path parameters` can be added to a resource method by specifying the parameter type and name in the resource path (eg: `albums/[string name]`). The `http` module supports `string`, `int`, `float`, `boolean`, and `decimal` types as path parameter types. Use it when designing REST API endpoints that require dynamic path segments. ::: code http_path_param.bal ::: Run the service as follows. ::: out http_path_param.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out http_path_param.client.out ::: >**Tip:** You can invoke the above service via the client given in the [HTTP client - Path parameter](/learn/by-example/http-client-path-parameter/) example. ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [HTTP service path parameter - Specification](/spec/http/#233-path-parameter) ================================================ FILE: examples/http-path-param/http_path_param.metatags ================================================ description: This example is on how to extract the path parameters in a Ballerina resource function. keywords: ballerina, ballerina by example, bbe, http, path param ================================================ FILE: examples/http-path-param/http_path_param.server.out ================================================ $ bal run path_param.bal ================================================ FILE: examples/http-path-param/tests/http_path_param_test.bal ================================================ import ballerina/http; import ballerina/test; @test:Config {} function testFunc() returns error? { http:Client httpEndpoint = check new("localhost:9090"); Album payload = check httpEndpoint->get("/albums/Blue Train"); test:assertEquals(payload, {title:"Blue Train", artist:"John Coltrane"}); Album|error response = httpEndpoint->get("/albums/abba"); if response is http:ClientRequestError { test:assertEquals(response.detail().statusCode, 404); } else { test:assertFail("Unexpected status code"); } } ================================================ FILE: examples/http-query-parameter/http_query_parameter.bal ================================================ import ballerina/http; type Album readonly & record {| string title; string artist; |}; table key(title) albums = table [ {title: "Blue Train", artist: "John Coltrane"}, {title: "Jeru", artist: "Gerry Mulligan"} ]; service / on new http:Listener(9090) { // The `artist` resource method argument is considered as the query parameter which is extracted from the request URI. resource function get albums(string artist) returns Album[] { return from Album album in albums where album.artist == artist select album; } } ================================================ FILE: examples/http-query-parameter/http_query_parameter.client.out ================================================ $ curl "http://localhost:9090/albums?artist=John%20Coltrane" [{"title":"Blue Train", "artist":"John Coltrane"}] ================================================ FILE: examples/http-query-parameter/http_query_parameter.md ================================================ # REST service - Query parameter The query parameter in the resource argument represents the query segment of the request URL. The argument name should be the key of the query, and its value is mapped during the runtime by extracting it from the URL. The query parameter does not need any additional annotation. The supported types are `string`, `int`, `float`, `boolean`, `decimal`, and `array` types of the aforementioned types. The query parameter type can be nilable (e.g., (`string? bar`)) and defaultable (e.g., (`string bar = "hello"`)). When a request contains query segments, retrieving them as resource arguments is much simpler and well-recommended. Alternatively, the `http:Request` also provides related methods to retrieve query parameters. ::: code http_query_parameter.bal ::: Run the service as follows. ::: out http_query_parameter.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out http_query_parameter.client.out ::: >**Tip:** You can invoke the above service via the client given in the [HTTP client - Query parameter](/learn/by-example/http-client-query-parameter/) example. ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [HTTP service query parameter - Specification](/spec/http/#2343-query-parameter) ================================================ FILE: examples/http-query-parameter/http_query_parameter.metatags ================================================ description: This example is on how to extract the query parameters in an HTTP resource function. keywords: ballerina, ballerina by example, bbe, http service, query parameter ================================================ FILE: examples/http-query-parameter/http_query_parameter.server.out ================================================ $ bal run http_query_parameter.bal ================================================ FILE: examples/http-query-parameter/tests/http_query_parameter_test.bal ================================================ import ballerina/http; import ballerina/test; @test:Config {} function testFunc() returns error? { http:Client httpEndpoint = check new ("localhost:9090"); json[] payload = check httpEndpoint->/albums(artist = "John Coltrane"); test:assertEquals(payload, [{title: "Blue Train", artist: "John Coltrane"}]); payload = check httpEndpoint->/albums(artist = "John"); test:assertEquals(payload, []); } ================================================ FILE: examples/http-request-interceptor/http_request_interceptor.bal ================================================ import ballerina/http; type Album readonly & record {| string title; string artist; |}; table key(title) albums = table [ {title: "Blue Train", artist: "John Coltrane"}, {title: "Jeru", artist: "Gerry Mulligan"} ]; // A `Requestinterceptorservice` class implementation. It intercepts the request // and adds a header before it is dispatched to the target service. service class RequestInterceptor { *http:RequestInterceptor; // A default resource method, which will be executed for all the requests. // A `RequestContext` is used to share data between the interceptors. // An accessor and a path can also be specified. In that case, the interceptor will be // executed only for the requests, which match the accessor and path. resource function 'default [string... path]( http:RequestContext ctx, @http:Header {name: "x-api-version"} string xApiVersion) returns http:NotImplemented|http:NextService|error? { // Checks the API version header. if xApiVersion != "v1" { // Returns a `501 NotImplemented` response if the version is not supported. return http:NOT_IMPLEMENTED; } // Returns the next interceptor or the target service in the pipeline. // An error is returned when the call fails. return ctx.next(); } } // Engage interceptors at the service level using an `http:InterceptableService`. The interceptors // will inherit the basepath of the service. service http:InterceptableService / on new http:Listener(9090) { // Creates the interceptor pipeline. The function can return a single interceptor or an array of // interceptors as the interceptor pipeline. If the interceptor pipeline is an array, then, the // request interceptor services will be executed from head to tail. public function createInterceptors() returns RequestInterceptor { return new RequestInterceptor(); } resource function get albums() returns Album[] { return albums.toArray(); } } ================================================ FILE: examples/http-request-interceptor/http_request_interceptor.client.out ================================================ $ curl -v http://localhost:9090/albums -H "x-api-version: v2" * Trying 127.0.0.1:9090... * Connected to localhost (127.0.0.1) port 9090 (#0) > GET /albums HTTP/1.1 > Host: localhost:9090 > User-Agent: curl/7.79.1 > Accept: */* > x-api-version: v2 > * Mark bundle as not supporting multiuse < HTTP/1.1 501 Not Implemented < content-length: 0 < server: ballerina < date: Wed, 14 Dec 2022 15:38:41 +0530 < ================================================ FILE: examples/http-request-interceptor/http_request_interceptor.md ================================================ # HTTP service - Request interceptor The `http:RequestInterceptor` is used to intercept the request and execute custom logic. A `RequestInterceptor` is a service object with only one resource method, which is executed before dispatching the request to the actual resource in the target service. This resource method can have parameters just like a usual resource method in an `http:Service`. A `RequestInterceptor` can be created from a service class, which includes the `http:RequestInterceptor` service type. Then, this service object can be engaged at the service level by declaring an `http:InterceptableService` object. This accepts an interceptor service object or an array of interceptor service objects as an interceptor pipeline, and the interceptors are executed in the order in which they are placed in the pipeline. Use `RequestInterceptors` to execute common logic such as logging, header manipulation, state publishing, etc., for inbound requests. ::: code http_request_interceptor.bal ::: Run the service as follows. ::: out http_request_interceptor.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out http_request_interceptor.client.out ::: >**Tip:** You can invoke the above service via the [Send request/Receive response client](/learn/by-example/http-client-send-request-receive-response/) example by adding the required header to the request. ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [HTTP service request interceptor - Specification](/spec/http/#811-request-interceptor) ================================================ FILE: examples/http-request-interceptor/http_request_interceptor.metatags ================================================ description: This example demonstrates the HTTP request interceptors in Ballerina. keywords: ballerina, ballerina by example, bbe, http, service, interceptor, request, filter ================================================ FILE: examples/http-request-interceptor/http_request_interceptor.server.out ================================================ $ bal run http_request_interceptor.bal ================================================ FILE: examples/http-request-interceptor/tests/http_request_interceptor_test.bal ================================================ import ballerina/http; import ballerina/test; @test:Config{} function testFunc() returns error? { http:Client clientEP = check new("localhost:9090"); http:Response res = check clientEP->get("/albums", {"x-api-version": "v1"}); test:assertEquals(res.statusCode, 200); res = check clientEP->get("/albums", {"x-api-version": "v2"}); test:assertEquals(res.statusCode, 501); } ================================================ FILE: examples/http-request-response/http_request_response.bal ================================================ import ballerina/http; type Album readonly & record {| string title; string artist; |}; table key(title) albums = table [ {title: "Blue Train", artist: "John Coltrane"}, {title: "Jeru", artist: "Gerry Mulligan"} ]; service / on new http:Listener(9090) { // The request is defined in the signature parameter. resource function post albums(http:Request request) returns http:Response|error { json payload = check request.getJsonPayload(); Album album = check payload.cloneWithType(); albums.add(album); // Create a response and populate the headers/payload. http:Response response = new; response.setPayload(album); response.setHeader("x-music-genre", "Jazz"); return response; } } ================================================ FILE: examples/http-request-response/http_request_response.client.out ================================================ $ curl http://localhost:9090/albums -H "Content-type:application/json" -d "{\"title\": \"Sarah Vaughan and Clifford Brown\", \"artist\": \"Sarah Vaughan\"}" {"title":"Sarah Vaughan and Clifford Brown", "artist":"Sarah Vaughan"} ================================================ FILE: examples/http-request-response/http_request_response.md ================================================ # HTTP service - Request/Response object `http:Request` and `http:Response` objects are Ballerina abstractions for HTTP request and HTTP response respectively. They are considered low-level abstractions which are used to implement high-level abstractions such as data-binding, header mapping, query parameter mapping, etc. They can be used both on the client side and the service side. They are useful when implementing advanced scenarios such as gateways, proxy services, handling multipart requests, etc. In most cases, the `http:Request` and the `http:Response` objects are not needed as higher-level abstractions can do the same thing. ::: code http_request_response.bal ::: Run the service as follows. ::: out http_request_response.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out http_request_response.client.out ::: >**Tip:** You can invoke the above service via the [Send request/Receive response client](/learn/by-example/http-client-send-request-receive-response/) example. ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [HTTP request and response - Specification](/spec/http/#6-request-and-response) ================================================ FILE: examples/http-request-response/http_request_response.metatags ================================================ description: This example is on the HTTP request and response usage in Ballerina. keywords: ballerina, ballerina by example, bbe, http, request, response ================================================ FILE: examples/http-request-response/http_request_response.server.out ================================================ $ bal run http_request_response.bal ================================================ FILE: examples/http-request-response/tests/http_request_response_test.bal ================================================ import ballerina/test; import ballerina/http; @test:Config {} function testFunc() returns error? { http:Client httpEndpoint = check new("localhost:9090"); Album lastAlbum = check httpEndpoint->post("/albums", {title:"Sarah Vaughan and Clifford Brown", artist:"Sarah Vaughan"}); test:assertEquals(lastAlbum, {title:"Sarah Vaughan and Clifford Brown", artist:"Sarah Vaughan"}); } ================================================ FILE: examples/http-request-with-multiparts/files/test.xml ================================================ 0.963 test xml file to be used as a file part ================================================ FILE: examples/http-request-with-multiparts/http_request_with_multiparts.1.client.out ================================================ $ curl -F "part1={\"name\":\"ballerina\"};type=application/json" http://localhost:9090/multiparts/decoder -H "Content-Type: multipart/mixed" -H 'Expect:' --f710b4a02896b88a content-disposition: attachment;name="part1" content-type: application/json content-id: 0 {"name":"ballerina"} --f710b4a02896b88a-- ================================================ FILE: examples/http-request-with-multiparts/http_request_with_multiparts.2.client.out ================================================ $ curl -v http://localhost:9090/multiparts/encoder > GET /multiparts/encoder HTTP/1.1 > Host: localhost:9090 > User-Agent: curl/7.64.1 > Accept: */* > < HTTP/1.1 200 OK < content-type: multipart/form-data; boundary=bd7547c98465dae2 < date: Wed, 23 Sep 2020 10:20:17 +0530 < server: ballerina < content-length: 398 < --bd7547c98465dae2 content-disposition: form-data;name="json part" content-type: application/json content-id: 0 {"name":"wso2"} --bd7547c98465dae2 content-disposition: form-data;name="xml file part" content-type: application/xml content-id: 1 0.963 test xml file to be used as a file part --bd7547c98465dae2-- * Connection #0 to host localhost left intact * Closing connection 0 ================================================ FILE: examples/http-request-with-multiparts/http_request_with_multiparts.bal ================================================ import ballerina/http; import ballerina/log; import ballerina/mime; service /multiparts on new http:Listener(9090) { resource function post decoder(http:Request request) returns http:Response|http:InternalServerError|error { var bodyParts = check request.getBodyParts(); foreach var part in bodyParts { handleContent(part); } http:Response response = new; response.setPayload(bodyParts); return response; } resource function get encoder(http:Request req) returns http:Response|http:InternalServerError|error { //Create a `json` body part. mime:Entity jsonBodyPart = new; jsonBodyPart.setContentDisposition(getContentDispositionForFormData("json part")); jsonBodyPart.setJson({"name": "wso2"}); //Create an `xml` body part as a file upload. mime:Entity xmlFilePart = new; xmlFilePart.setContentDisposition(getContentDispositionForFormData("xml file part")); // This file path is relative to where Ballerina is running. // If your file is located outside, // give the absolute file path instead. xmlFilePart.setFileAsEntityBody("./files/test.xml", contentType = mime:APPLICATION_XML); // Create an array to hold all the body parts. mime:Entity[] bodyParts = [jsonBodyPart, xmlFilePart]; http:Request request = new; // Set the body parts to the request. // Here the content-type is set as multipart form data. // This also works with any other multipart media type. // E.g., `multipart/mixed`, `multipart/related` etc. // You need to pass the content type that suits your requirement. request.setBodyParts(bodyParts, contentType = mime:MULTIPART_FORM_DATA); http:Client httpClient = check new ("localhost:9090"); http:Response returnResponse = check httpClient->/multiparts/decoder.post(request); return returnResponse; } } function handleContent(mime:Entity bodyPart) { // Get the media type from the body part retrieved from the request. var mediaType = mime:getMediaType(bodyPart.getContentType()); if mediaType is mime:MediaType { string baseType = mediaType.getBaseType(); if mime:APPLICATION_XML == baseType || mime:TEXT_XML == baseType { var payload = bodyPart.getXml(); if payload is xml { log:printInfo(payload.toString()); } else { log:printError(payload.message()); } } else if mime:APPLICATION_JSON == baseType { var payload = bodyPart.getJson(); if payload is json { log:printInfo(payload.toJsonString()); } else { log:printError(payload.message()); } } else if mime:TEXT_PLAIN == baseType { var payload = bodyPart.getText(); if payload is string { log:printInfo(payload); } else { log:printError(payload.message()); } } } } function getContentDispositionForFormData(string partName) returns (mime:ContentDisposition) { mime:ContentDisposition contentDisposition = new; contentDisposition.name = partName; contentDisposition.disposition = "form-data"; return contentDisposition; } ================================================ FILE: examples/http-request-with-multiparts/http_request_with_multiparts.md ================================================ # HTTP client - Request with multiparts The multipart payload is one or more different sets of data combined in a single body. The `http:Client` supports multipart content setting and retrieving in the `http:Request` along with the nested parts through support functions. An array of `mime:Entity` is returned when retrieving parts through `getBodyParts` method of the `http:Request`. If the received parts contain nested parts, you can loop through the parent parts and get the child parts. When sending out multipart content, `setBodyParts` is used to set the array of `mime:Entity`. This is useful to handle different content-typed messages as a single payload and large payloads. ::: code http_request_with_multiparts.bal ::: Run the service as follows. ::: out http_request_with_multiparts.server.out ::: ## Prerequisites In the directory, which contains the `.bal` file, create a directory named `files`, and add an XML file named `test.xml` in it. Invoke the service by executing the following cURL command in a new terminal. ::: out http_request_with_multiparts.1.client.out ::: Execute the following cURL command to encode the parts of the body and send a multipart request via the Ballerina service. ::: out http_request_with_multiparts.2.client.out ::: ## Related links - [`setBodyParts()` - API documentation](https://lib.ballerina.io/ballerina/http/latest#Request#setBodyParts) - [HTTP client supported-multipart-types - Specification](/spec/mime/#3-supported-multipart-types) ================================================ FILE: examples/http-request-with-multiparts/http_request_with_multiparts.metatags ================================================ description: This example is on how Ballerina supports encoding and decoding multipart content in HTTP requests along with nested parts. keywords: ballerina, ballerina by example, bbe, mime, multiparts, entity ================================================ FILE: examples/http-request-with-multiparts/http_request_with_multiparts.server.out ================================================ $ bal run request_with_multiparts.bal time = 2021-01-21 22:00:17,167 level = INFO module = "" message = "{"name":"ballerina"}" time = 2021-01-21 22:01:18,466 level = INFO module = "" message = "{"name":"wso2"}" time = 2021-01-21 22:01:18,682 level = INFO module = "" message = " 0.963 test xml file to be used as a file part " ================================================ FILE: examples/http-request-with-multiparts/tests/http_request_with_multiparts_test.bal ================================================ import ballerina/mime; import ballerina/http; import ballerina/test; @test:Config {} function testFunc() returns error? { http:Client httpEP = check new ("localhost:9090"); mime:Entity jsonBodyPart = new; jsonBodyPart.setJson({"name": "ballerina"}); mime:Entity[] bodyParts = [jsonBodyPart]; http:Request request = new; request.setBodyParts(bodyParts, contentType = mime:MULTIPART_FORM_DATA); http:Response response1 = check httpEP->post("/multiparts/decoder", request); mime:Entity[] result = check response1.getBodyParts(); json jsonValue = check result[0].getJson(); test:assertEquals(jsonValue.toJsonString(), "{\"name\":\"ballerina\"}"); http:Response response2 = check httpEP->get("/multiparts/encoder"); result = check response2.getBodyParts(); jsonValue = check result[0].getJson(); test:assertEquals(jsonValue.toString(), "{\"name\":\"wso2\"}"); xml xmlValue = check result[1].getXml(); xml element1 = xmlValue.selectDescendants("version"); test:assertEquals(element1.data(), "0.963"); xml element2 = xmlValue.selectDescendants("test"); test:assertEquals(element2.data(), "test xml file to be used as a file part"); } ================================================ FILE: examples/http-response-interceptor/http_response_interceptor.bal ================================================ import ballerina/http; type Album readonly & record {| string title; string artist; |}; table key(title) albums = table [ {title: "Blue Train", artist: "John Coltrane"}, {title: "Jeru", artist: "Gerry Mulligan"} ]; // A `ResponseInterceptor` service class implementation. It intercepts the response // and adds a header before it is dispatched to the client. service class ResponseInterceptor { *http:ResponseInterceptor; // The `interceptResponse` remote method will be executed for all the // responses. A `RequestContext` is used to share data between interceptors. remote function interceptResponse(http:RequestContext ctx, http:Response res) returns http:NextService|error? { // Sets a header to the response inside the interceptor service. res.setHeader("x-api-version", "v2"); // Returns the next interceptor in the pipeline or `nil` if there is no // more interceptors to be returned. In case a `nil` value is returned, then, // the modified response will be returned to the client. In addition to these // return values, an error is returned when the call fails. return ctx.next(); } } // Engage interceptors at the service level using an `http:InterceptableService`. The base path of the // interceptor services is the same as the target service. Hence, they will be executed only for // this particular service. service http:InterceptableService / on new http:Listener(9090) { // Creates the interceptor pipeline. The function can return a single interceptor or an array of // interceptors as the interceptor pipeline. If the interceptor pipeline is an array, then, the // request interceptor services will be executed from head to tail. public function createInterceptors() returns ResponseInterceptor { return new ResponseInterceptor(); } resource function get albums() returns Album[] { return albums.toArray(); } } ================================================ FILE: examples/http-response-interceptor/http_response_interceptor.client.out ================================================ $ curl -v http://localhost:9090/albums * Trying 127.0.0.1:9090... * Connected to localhost (127.0.0.1) port 9090 (#0) > GET /albums HTTP/1.1 > Host: localhost:9090 > User-Agent: curl/7.79.1 > Accept: */* > * Mark bundle as not supporting multiuse < HTTP/1.1 200 OK < content-type: application/json < x-api-version: v2 < content-length: 95 < server: ballerina < date: Wed, 14 Dec 2022 11:51:35 +0530 < [{"title":"Blue Train", "artist":"John Coltrane"}, {"title":"Jeru", "artist":"Gerry Mulligan"}] ================================================ FILE: examples/http-response-interceptor/http_response_interceptor.md ================================================ # HTTP service - Response interceptor The `http:ResponseInterceptor` is used to intercept the response and execute custom logic. A `ResponseInterceptor` is a service object with a remote method called `interceptResponse`, which is executed before dispatching the response to the client. A `ResponseInterceptor` can be created from a service class, which includes the `http:ResponseInterceptor` service type. This service object can be engaged at the service level by declaring an `http:InterceptableService` object. This accepts an interceptor service object or an array of interceptor service objects as an interceptor pipeline and the interceptors are executed in the order in which they are placed in the pipeline. Use `ResponseInterceptors` to execute common logic such as logging, header manipulation, state publishing, etc., for all outbound responses. ::: code http_response_interceptor.bal ::: Run the service as follows. ::: out http_response_interceptor.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out http_response_interceptor.client.out ::: >**Tip:** You can invoke the above service via the [Send request/Receive response client](/learn/by-example/http-client-send-request-receive-response/). ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [HTTP service response interceptor - Specification](/spec/http/#812-response-interceptor) ================================================ FILE: examples/http-response-interceptor/http_response_interceptor.metatags ================================================ description: This example demonstrates the HTTP response interceptors in Ballerina. keywords: ballerina, ballerina by example, bbe, http, service, interceptor, response, filter ================================================ FILE: examples/http-response-interceptor/http_response_interceptor.server.out ================================================ $ bal run http_response_interceptor.bal ================================================ FILE: examples/http-response-interceptor/tests/http_response_interceptor_test.bal ================================================ import ballerina/http; import ballerina/test; @test:Config{} function testFunc() returns error? { http:Client clientEP = check new("localhost:9090"); http:Response res = check clientEP->get("/albums"); test:assertEquals(res.statusCode, 200); test:assertEquals(check res.getHeader("x-api-version"), "v2"); } ================================================ FILE: examples/http-response-with-multiparts/files/test.xml ================================================ 0.963 test xml file to be used as a file part ================================================ FILE: examples/http-response-with-multiparts/http_response_with_multiparts.1.client.out ================================================ $ curl http://localhost:9092/multiparts/encoder --646e483fc8826c55 content-type: multipart/mixed;boundary=e8a931f5e25d263e --e8a931f5e25d263e content-type: application/json {"name":"wso2"} --e8a931f5e25d263e content-type: text/xml 0.963 test xml file to be used as a file part --e8a931f5e25d263e-- --646e483fc8826c55-- ================================================ FILE: examples/http-response-with-multiparts/http_response_with_multiparts.2.client.out ================================================ $ curl http://localhost:9090/multiparts/decoder Body Parts Received! ================================================ FILE: examples/http-response-with-multiparts/http_response_with_multiparts.bal ================================================ import ballerina/http; import ballerina/io; import ballerina/log; import ballerina/mime; service /multiparts on new http:Listener(9092) { resource function get encoder() returns http:Response { // Creates an enclosing entity to hold the child parts. mime:Entity parentPart = new; mime:Entity childPart1 = new; childPart1.setJson({"name": "wso2"}); mime:Entity childPart2 = new; // This file path is relative to where the Ballerina is running. // If your file is located outside, give the absolute file path instead. childPart2.setFileAsEntityBody("./files/test.xml", contentType = mime:TEXT_XML); mime:Entity[] childParts = [childPart1, childPart2]; parentPart.setBodyParts(childParts, contentType = mime:MULTIPART_MIXED); // Creates an array to hold the parent part and set it to the response. mime:Entity[] immediatePartsToResponse = [parentPart]; http:Response outResponse = new; outResponse.setBodyParts(immediatePartsToResponse, contentType = mime:MULTIPART_FORM_DATA); return outResponse; } } service /multiparts on new http:Listener(9090) { // This resource accepts multipart responses. resource function get decoder() returns string|http:InternalServerError|error { http:Client httpClient = check new ("localhost:9092"); http:Response returnResult = check httpClient->/multiparts/encoder; mime:Entity[] parentParts = check returnResult.getBodyParts(); foreach var parentPart in parentParts { handleNestedParts(parentPart); } return "Body Parts Received!"; } } function handleNestedParts(mime:Entity parentPart) { string contentTypeOfParent = parentPart.getContentType(); if contentTypeOfParent.startsWith("multipart/") { var childParts = parentPart.getBodyParts(); if childParts is mime:Entity[] { log:printInfo("Nested Parts Detected!"); foreach var childPart in childParts { handleContent(childPart); } } else { log:printError("Error retrieving child parts! " + childParts.message()); } } } function handleContent(mime:Entity bodyPart) { string baseType = getBaseType(bodyPart.getContentType()); if mime:APPLICATION_XML == baseType || mime:TEXT_XML == baseType { var payload = bodyPart.getXml(); if payload is xml { log:printInfo("XML data: " + payload.toString()); } else { log:printError("Error in parsing XML data", 'error = payload); } } else if mime:APPLICATION_JSON == baseType { var payload = bodyPart.getJson(); if payload is json { log:printInfo("JSON data: " + payload.toJsonString()); } else { log:printError("Error in parsing JSON data", 'error = payload); } } else if mime:TEXT_PLAIN == baseType { var payload = bodyPart.getText(); if payload is string { log:printInfo("Text data: " + payload); } else { log:printError("Error in parsing text data", 'error = payload); } } else if mime:APPLICATION_PDF == baseType { var payload = bodyPart.getByteStream(); if payload is stream { // Writes the incoming stream to a file using the `io:fileWriteBlocksFromStream` API by providing the // file location to which the content should be written. io:Error? result = io:fileWriteBlocksFromStream("./files/ReceivedFile.pdf", payload); if result is error { log:printError("Error occurred while writing ", 'error = result); } close(payload); } else { log:printError("Error in parsing byte channel :", 'error = payload); } } } function getBaseType(string contentType) returns string { var result = mime:getMediaType(contentType); if result is mime:MediaType { return result.getBaseType(); } panic result; } function close(stream byteStream) { var cr = byteStream.close(); if cr is error { log:printError("Error occurred while closing the stream: ", 'error = cr); } } ================================================ FILE: examples/http-response-with-multiparts/http_response_with_multiparts.md ================================================ # HTTP service - Response with multiparts The multipart payload is one or more different sets of data combined in a single body. HTTP service supports multipart content setting and retrieving in the `http:Response` along with the nested parts through support functions. An array of `mime:Entity` is returned when retrieving parts through `getBodyParts` method of the `http:Response`. If the received parts contain nested parts, you can loop through the parent parts and get the child parts. When sending out multipart content, `setBodyParts` is used to set the array of `mime:Entity`. This is useful to handle different content-typed messages as a single payload and large payloads. ::: code http_response_with_multiparts.bal ::: Run the service as follows. ::: out http_response_with_multiparts.server.out ::: ## Prerequisites In the directory, which contains the `.bal` file, create a directory named `files`, and add an XML file named `test.xml` in it. Invoke the service by executing the following cURL command in a new terminal. ::: out http_response_with_multiparts.1.client.out ::: To decode the inbound response with multiparts, execute the following cURL command. ::: out http_response_with_multiparts.2.client.out ::: ## Related links - [`setBodyParts()` - API documentation](https://lib.ballerina.io/ballerina/mime/latest#Entity#setBodyParts) - [HTTP service supported-multipart-types - Specification](/spec/mime/#3-supported-multipart-types) ================================================ FILE: examples/http-response-with-multiparts/http_response_with_multiparts.metatags ================================================ description: This example is on how Ballerina supports encoding and decoding multipart content in HTTP responses along with nested parts. keywords: ballerina, ballerina by example, bbe, mime, multiparts, entity ================================================ FILE: examples/http-response-with-multiparts/http_response_with_multiparts.server.out ================================================ $ bal run response_with_multiparts.bal time = 2021-01-21 22:20:38,143 level = INFO module = "" message = "Nested Parts Detected!" time = 2021-01-21 22:20:38,185 level = INFO module = "" message = "JSON data: {"name":"wso2"}" time = 2021-01-21 22:20:38,324 level = INFO module = "" message = "XML data: 0.963 test xml file to be used as a file part " ================================================ FILE: examples/http-response-with-multiparts/tests/http_response_with_multiparts_test.bal ================================================ import ballerina/mime; import ballerina/http; import ballerina/test; @test:Config {} function testFunc() returns error? { http:Client httpEP1 = check new ("localhost:9090"); http:Client httpEP2 = check new ("localhost:9092"); string response1 = check httpEP1->get("/multiparts/decoder"); test:assertEquals(response1, "Body Parts Received!"); http:Response response2 = check httpEP2->get("/multiparts/encoder"); mime:Entity[] parentParts = check response2.getBodyParts(); mime:Entity[] childParts = check parentParts[0].getBodyParts(); json jsonValue = check childParts[0].getJson(); test:assertEquals(jsonValue.toString(), "{\"name\":\"wso2\"}"); xml xmlValue = check childParts[1].getXml(); xml element1 = xmlValue.selectDescendants("version"); test:assertEquals(element1.data(), "0.963"); xml element2 = xmlValue.selectDescendants("test"); test:assertEquals(element2.data(), "test xml file to be used as a file part"); } ================================================ FILE: examples/http-restrict-by-media-type/http_restrict_by_media_type.bal ================================================ import ballerina/http; service / on new http:Listener(9090) { // The `consumes` and `produces` annotations of the resource configuration contains MIME types as // an array of strings. The resource can only consume/accept `text/plain` media type. Therefore, // the `Content-Type` header of the request must be `text/plain` types. The resource can produce // `application/xml` payloads. Therefore, you need to set the `Accept` header accordingly. @http:ResourceConfig { consumes: ["text/plain"], produces: ["application/xml"] } resource function post transform(@http:Payload string msg) returns xml|http:InternalServerError { if re `^[a-zA-Z]*$`.isFullMatch(msg) { return xml `${msg}`; } return { body: xml `invalid string`}; } } ================================================ FILE: examples/http-restrict-by-media-type/http_restrict_by_media_type.client.out ================================================ $ curl -v http://localhost:9090/transform -H "Accept:application/xml" -H "Content-Type:text/plain" -d 'Ballerina' * Trying ::1... * TCP_NODELAY set * Connected to localhost (::1) port 9090 (#0) > POST /transform HTTP/1.1 > Host: localhost:9090 > User-Agent: curl/7.64.1 > Accept:application/xml > Content-Type:text/plain > Content-Length: 9 > * upload completely sent off: 9 out of 9 bytes < HTTP/1.1 201 Created < content-type: application/xml < content-length: 22 < server: ballerina < date: Sat, 22 Oct 2022 17:53:21 +0530 < * Connection #0 to host localhost left intact Ballerina* Closing connection 0 # To invoke the service using an unsupported media type, execute the following cURL request. The content type of the # request is not listed under the `consumes` resource configuration. $ curl -v http://localhost:9090/transform -H "Accept:application/xml" -H "Content-Type:application/json" -d '{"name":"Ballerina"}' * Trying ::1... * TCP_NODELAY set * Connected to localhost (::1) port 9090 (#0) > POST /transform HTTP/1.1 > Host: localhost:9090 > User-Agent: curl/7.64.1 > Accept:application/xml > Content-Type:application/json > Content-Length: 20 > * upload completely sent off: 20 out of 20 bytes < HTTP/1.1 415 Unsupported Media Type < content-type: text/plain < content-length: 48 < server: ballerina < date: Sat, 22 Oct 2022 17:56:40 +0530 < * Connection #0 to host localhost left intact content-type : application/json is not supported* Closing connection 0 # To invoke the service with a media type that is not acceptable, execute the following cURL request. The media type mentioned # in the `Accept` header is not listed under the `produces` resource configuration. $ curl -v http://localhost:9090/transform -H "Accept:text/html" -H "Content-Type:text/plain" -d 'Ballerina' * Trying ::1... * TCP_NODELAY set * Connected to localhost (::1) port 9090 (#0) > POST /transform HTTP/1.1 > Host: localhost:9090 > User-Agent: curl/7.64.1 > Accept:text/html > Content-Type:text/plain > Content-Length: 9 > * upload completely sent off: 9 out of 9 bytes < HTTP/1.1 406 Not Acceptable < content-type: text/plain < content-length: 0 < server: ballerina < date: Sat, 22 Oct 2022 17:58:28 +0530 < * Connection #0 to host localhost left intact * Closing connection 0 ================================================ FILE: examples/http-restrict-by-media-type/http_restrict_by_media_type.md ================================================ # HTTP service - Restrict by media type The content negotiation for the REST API design is achieved via the `consumes` and `produces` configurations. The resource accepting request content type is defined under the `consumes` and the resource producing response content type is defined under the `produces` in the resource configuration. Each configuration is checked against the `Accept` header and the `Content-type` header of the request. If the negotiation fails, the error response is returned with `406 Not Acceptable` or `415 Unsupported` status codes respectively. ::: code http_restrict_by_media_type.bal ::: Run the service as follows. ::: out http_restrict_by_media_type.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out http_restrict_by_media_type.client.out ::: ## Related links - [`http:ResourceConfig` - API documentation](https://lib.ballerina.io/ballerina/http/latest#HttpResourceConfig) - [HTTP service resource configuration - Specification](/spec/http/#42-resource-configuration) ================================================ FILE: examples/http-restrict-by-media-type/http_restrict_by_media_type.metatags ================================================ description: This example is on how resources can be configured to restrict the types of media they consume and produce in Ballerina. keywords: ballerina, ballerina by example, bbe, http, content-type, accept ================================================ FILE: examples/http-restrict-by-media-type/http_restrict_by_media_type.server.out ================================================ $ bal run restrict_by_media_type.bal ================================================ FILE: examples/http-restrict-by-media-type/tests/http_restrict_by_media_type_test.bal ================================================ import ballerina/http; import ballerina/test; @test:Config {} function testFunc() returns error? { http:Client testClient = check new("localhost:9090"); xml payload = check testClient->post("/transform", "Ballerina"); test:assertEquals(payload, xml `Ballerina`); xml|error err = testClient->post("/transform", "WSO2"); if err is http:RemoteServerError { test:assertEquals(err.detail().body, xml `invalid string`); } } ================================================ FILE: examples/http-retry/http_retry.bal ================================================ import ballerina/http; import ballerina/io; type Album readonly & record { string title; string artist; }; public function main() returns error? { http:Client albumClient = check new ("localhost:9090", retryConfig = { // The initial retry interval in seconds. interval: 3, // The number of retry attempts before stopping. count: 3, // The multiplier of the retry interval exponentially increases the retry interval. backOffFactor: 2.0, // The upper limit of the retry interval is in seconds. If the `interval` into the `backOffFactor` // value exceeded the `maxWaitInterval` interval value, `maxWaitInterval` is considered as the retry interval. maxWaitInterval: 20 } ); Album[] payload = check albumClient->/albums; io:println(payload); } ================================================ FILE: examples/http-retry/http_retry.md ================================================ # HTTP client - Retry The HTTP retry client tries sending over the same request to the backend service when there is a network-level failure. The retry is configured in the `retryConfig` field of the client configuration. ::: code http_retry.bal ::: ## Prerequisites - Run the HTTP service given in the [Basic REST service](/learn/by-example/http-basic-rest-service/) example. Run the program by executing the following command. ::: out http_retry.out ::: ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [HTTP client retry - Specification](/spec/http/#2414-retry) ================================================ FILE: examples/http-retry/http_retry.metatags ================================================ description: This example is on how to use an HTTP retry client in Ballerina to automatically retry when an erroneous response is received. keywords: ballerina, ballerina by examples, bbe, http, resiliency, retry ================================================ FILE: examples/http-retry/http_retry.out ================================================ $ bal run http_retry.bal ================================================ FILE: examples/http-retry/tests/http_retry_test.bal ================================================ import ballerina/http; import ballerina/lang.runtime; import ballerina/log; import ballerina/test; @test:Config {} function testFunc() returns error? { http:Client backendClientEP = check new ("localhost:8080", retryConfig = { interval: 3, count: 3, backOffFactor: 2.0, maxWaitInterval: 20 }, timeout = 2 ); string payload = check backendClientEP->/greeting; test:assertEquals(payload, "Hello World!!!"); } service / on new http:Listener(8080) { private int counter = 0; resource function get greeting() returns string { self.counter += 1; // Delay the response by 5 seconds to mimic network-level delays. if self.counter % 4 != 0 { log:printInfo("Request received from the client to delayed service."); runtime:sleep(5); return "Hello World!!!"; } else { log:printInfo("Request received from the client to healthy service."); return "Hello World!!!"; } } } ================================================ FILE: examples/http-send-different-status-codes/http_send_different_status_codes.bal ================================================ import ballerina/http; type Album readonly & record {| string title; string artist; |}; table key(title) albums = table [ {title: "Blue Train", artist: "John Coltrane"}, {title: "Jeru", artist: "Gerry Mulligan"} ]; service / on new http:Listener(9090) { // The resource returns the `409 Conflict` status code as the error response status code using // the `StatusCodeResponse` constants. This constant does not have a body or headers. resource function post albums(Album album) returns Album|http:Conflict { if albums.hasKey(album.title) { return http:CONFLICT; } albums.add(album); return album; } } ================================================ FILE: examples/http-send-different-status-codes/http_send_different_status_codes.client.out ================================================ $ curl http://localhost:9090/albums -H "Content-type:application/json" -d "{\"title\": \"Blue Train\", \"artist\": \"John Coltrane\"}" -v > POST /albums HTTP/1.1 > Host: localhost:9090 > User-Agent: curl/7.79.1 > Accept: */* > Content-type:application/json > Content-Length: 50 > * Mark bundle as not supporting multiuse < HTTP/1.1 409 Conflict < content-length: 0 < server: ballerina < date: Mon, 5 Dec 2022 16:23:51 +0530 < ================================================ FILE: examples/http-send-different-status-codes/http_send_different_status_codes.md ================================================ # REST service - Send different status codes The subtypes of the `http:StatusCodeResponse` record type represent different HTTP status code responses. Returning them from the resource method results in the relevant HTTP status code response. To send a non-entity body response, use the relevant constant value declared in the `http` module. These constant values can be directly returned from the resource method by specifying the relevant return type in the resource method signature. Use this when different status code responses need to be sent without a body and headers. ::: code http_send_different_status_codes.bal ::: Run the service as follows. ::: out http_send_different_status_codes.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out http_send_different_status_codes.client.out ::: >**Tip:** You can invoke the above service via the [Send request/Receive response client](/learn/by-example/http-client-send-request-receive-response/) example. ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [HTTP service status code response - Specification](/spec/http/#2351-status-code-response) ================================================ FILE: examples/http-send-different-status-codes/http_send_different_status_codes.metatags ================================================ description: This example is on how to use status code records in an HTTP resource method. keywords: ballerina, ballerina by example, bbe, http resource, return types, statusCode records ================================================ FILE: examples/http-send-different-status-codes/http_send_different_status_codes.server.out ================================================ $ bal run send_different_status_code.bal ================================================ FILE: examples/http-send-different-status-codes/tests/http_send_different_status_codes_test.bal ================================================ import ballerina/http; import ballerina/test; @test:Config {} function testFunc() returns error? { http:Client testClient = check new("localhost:9090"); http:Response response = check testClient->post("/albums", {title:"Sarah Vaughan and Clifford Brown", artist:"Sarah Vaughan"}); test:assertEquals(response.statusCode, 201); response = check testClient->post("/albums", {title:"Blue Train", artist:"John Coltrane"}); test:assertEquals(response.statusCode, 409); } ================================================ FILE: examples/http-send-different-status-codes-with-payload/http_send_different_status_codes_with_payload.bal ================================================ import ballerina/http; type Album readonly & record {| string title; string artist; |}; // Represents the subtype of http:Conflict status code record. type AlbumConflict record {| *http:Conflict; record { string message; } body; |}; table key(title) albums = table [ {title: "Blue Train", artist: "John Coltrane"}, {title: "Jeru", artist: "Gerry Mulligan"} ]; service / on new http:Listener(9090) { // The resource returns the `409 Conflict` status code as the error response status code using the built-in `StatusCodeResponse`. resource function post albums(Album album) returns Album|AlbumConflict { if albums.hasKey(album.title) { return {body: { message: "album already exists" }}; } albums.add(album); return album; } } ================================================ FILE: examples/http-send-different-status-codes-with-payload/http_send_different_status_codes_with_payload.client.out ================================================ $ curl http://localhost:9090/albums -H "Content-type:application/json" -d "{\"title\": \"Blue Train\", \"artist\": \"John Coltrane\"}" -v > POST /albums HTTP/1.1 > Host: localhost:9090 > User-Agent: curl/7.64.1 > Accept: */* > Content-type:application/json > Content-Length: 50 > < HTTP/1.1 409 Conflict < content-type: application/json < content-length: 36 < server: ballerina < date: Thu, 17 Nov 2022 11:13:50 +0530 < {"message":"album already exists"} ================================================ FILE: examples/http-send-different-status-codes-with-payload/http_send_different_status_codes_with_payload.md ================================================ # REST service - Send different status codes with payload The resource method can return a subtype of the `http:StatusCodeResponse` record type with a body and headers. This type can be created by including a subtype of the `http:StatusCodeResponse` record type. The `body` field represents the response payload, while the `headers` field represents a `map` of the response headers. In addition, the `body` field type can be overridden by a custom record type. Use these custom subtypes if different status code responses need to be sent with custom payload types and headers. Furthermore, defining such records can produce a better representation of the responses in the OpenAPI specification, and using typed records for the `body` field provides compiler validations and better tooling support. ::: code http_send_different_status_codes_with_payload.bal ::: Run the service as follows. ::: out http_send_different_status_codes_with_payload.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out http_send_different_status_codes_with_payload.client.out ::: >**Tip:** You can invoke the above service via the [Send request/Receive response client](/learn/by-example/http-client-send-request-receive-response/) example. ## Related links - [`http:StatusCodeResponse` type - API documentation](https://lib.ballerina.io/ballerina/http/latest#StatusCodeResponse) - [HTTP service status code response - Specification](/spec/http/#2351-status-code-response) ================================================ FILE: examples/http-send-different-status-codes-with-payload/http_send_different_status_codes_with_payload.metatags ================================================ description: This example is on how to use the status code records with a payload in an HTTP resource method. keywords: ballerina, ballerina by example, bbe, http resource, return types, statusCode records ================================================ FILE: examples/http-send-different-status-codes-with-payload/http_send_different_status_codes_with_payload.server.out ================================================ $ bal run send_different_status_code_with_payload.bal ================================================ FILE: examples/http-send-different-status-codes-with-payload/tests/http_send_different_status_codes_with_payload_test.bal ================================================ import ballerina/http; import ballerina/test; @test:Config {} function testFunc() returns error? { http:Client testClient = check new("localhost:9090"); http:Response response = check testClient->post("/albums", {title:"Sarah Vaughan and Clifford Brown", artist:"Sarah Vaughan"}); test:assertEquals(response.statusCode, 201); response = check testClient->post("/albums", {title:"Blue Train", artist:"John Coltrane"}); test:assertEquals(response.statusCode, 409); } ================================================ FILE: examples/http-send-header/http_send_header.bal ================================================ import ballerina/http; type Album readonly & record {| string title; string artist; |}; table key(title) albums = table [ {title: "Blue Train", artist: "John Coltrane"}, {title: "Jeru", artist: "Gerry Mulligan"} ]; type AlbumOk record {| *http:Ok; record {| string x\-music\-genre; |} headers; Album[] body; |}; service / on new http:Listener(9090) { resource function get albums() returns AlbumOk { return { headers: { x\-music\-genre: "Jazz" }, body: albums.toArray() }; } } ================================================ FILE: examples/http-send-header/http_send_header.client.out ================================================ $ curl -v localhost:9090/albums > GET /albums HTTP/1.1 > Host: localhost:9090 > User-Agent: curl/7.64.1 > Accept: */* > < HTTP/1.1 200 OK < x-music-genre: Jazz < content-type: application/json < content-length: 95 < server: ballerina < date: Mon, 9 Jan 2023 10:35:56 +0530 < [{"title":"Blue Train", "artist":"John Coltrane"}, {"title":"Jeru", "artist":"Gerry Mulligan"}] ================================================ FILE: examples/http-send-header/http_send_header.md ================================================ # HTTP service - Sending header The headers are used to send additional details along with the payload. To send custom headers, it is best to create a subtype of the relevant `http:StatusCodeResponse` record by specifying the required header. Creating a subtype helps accurately generate the OpenAPI specification which then can be used to generate the relevant clients. The type of the header can be one of `string`, `int`, `boolean`, `string[]`, `int[]` or `boolean[]`. ::: code http_send_header.bal ::: Run the service as follows. ::: out http_send_header.server.out ::: Invoke the HTTP GET resource by executing the following cURL command in a new terminal. ::: out http_send_header.client.out ::: >**Tip:** You can invoke the above service via the [Send request/Receive response client](/learn/by-example/http-client-send-request-receive-response/) example. ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [HTTP service status code response - Specification](/spec/http/#2351-status-code-response) ================================================ FILE: examples/http-send-header/http_send_header.metatags ================================================ description: This example demonstrates sending a custom HTTP header from a service in Ballerina. keywords: ballerina, ballerina by example, bbe, http service, header, response ================================================ FILE: examples/http-send-header/http_send_header.server.out ================================================ $ bal run http_send_header.bal ================================================ FILE: examples/http-send-header/tests/http_send_header_test.bal ================================================ import ballerina/test; import ballerina/http; @test:Config {} function testFunc() returns error? { http:Client httpEndpoint = check new("localhost:9090"); http:Response response = check httpEndpoint->get("/albums"); test:assertEquals(response.getHeader("x-music-genre"), "Jazz"); } ================================================ FILE: examples/http-send-response/http_send_response.bal ================================================ import ballerina/http; type Album readonly & record {| string title; string artist; |}; table key(title) albums = table [ {title: "Blue Train", artist: "John Coltrane"}, {title: "Jeru", artist: "Gerry Mulligan"} ]; service / on new http:Listener(9090) { // The resource returns the `Album` typed array value. resource function get albums() returns Album[] { return albums.toArray(); } } ================================================ FILE: examples/http-send-response/http_send_response.client.out ================================================ $ curl http://localhost:9090/albums -v > GET /albums HTTP/1.1 > Host: localhost:9090 > User-Agent: curl/7.64.1 > Accept: */* > < HTTP/1.1 200 OK < content-type: application/json < content-length: 95 < server: ballerina < date: Mon, 14 Nov 2022 15:08:04 +0530 < [{"title":"Blue Train", "artist":"John Coltrane"}, {"title":"Jeru", "artist":"Gerry Mulligan"}] ================================================ FILE: examples/http-send-response/http_send_response.md ================================================ # REST service - Send response Returning an `anydata` type from the resource method results in an HTTP response, where the returned value becomes the body. If the returned type is `nil`, then a `202 Accepted` response is returned to the client without the body. Otherwise, the response contains the returned value as the payload, and the `Content-type` header is inferred from the return type. In addition, the response status code is `201 Created` for `POST` resources and `200 Ok` for other resources. Furthermore, the `@http:Payload` annotation on the return type can be used to override the `Content-type` header. Returning an `anydata` type from the resource method is useful when the desired payload with the default status code and headers needs to be sent as the response. ::: code http_send_response.bal ::: Run the service as follows. ::: out http_send_response.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out http_send_response.client.out ::: >**Tip:** You can invoke the above service via the [Payload data binding client](/learn/by-example/http-client-data-binding/) example. ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [HTTP service return types - Specification](/spec/http/#235-return-types) ================================================ FILE: examples/http-send-response/http_send_response.metatags ================================================ description: This example is on how to return an anydata response from an HTTP resource function. keywords: ballerina, ballerina by example, bbe, http resource, return types, anydata ================================================ FILE: examples/http-send-response/http_send_response.server.out ================================================ $ bal run send_response.bal ================================================ FILE: examples/http-send-response/tests/http_send_response_test.bal ================================================ import ballerina/http; import ballerina/test; @test:Config {} function testFunc() returns error? { http:Client testClient = check new("localhost:9090"); Album[] payload = check testClient->get("/albums"); test:assertEquals(payload, [{title:"Blue Train",artist:"John Coltrane"},{title:"Jeru",artist:"Gerry Mulligan"}]); } ================================================ FILE: examples/http-service-and-resource-paths/http_service_and_resource_paths.bal ================================================ import ballerina/http; type Album readonly & record {| string title; string artist; |}; table key(title) albums = table [ {title: "Blue Train", artist: "John Coltrane"}, {title: "Jeru", artist: "Gerry Mulligan"} ]; // The `service path` represents the absolute path to the service. // If the `service path` is omitted, then it defaults to `/`. // It can be represented by both identifiers and string literals. E.g., `/music\-info`, `"/music-info"`. service /info on new http:Listener(9090) { // The `resource path` represents the relative path to the resource, and the `resource accessor` // represents the HTTP method used to access the resource. // Here, the resource path is `/albums`, and the resource accessor is `get`. // This means the resource is invoked when an HTTP GET request is made to `/info/albums`. // The `resource path` can be set as `.` to represent a resource with the `service path` // that is `/info`. resource function get albums() returns Album[] { return albums.toArray(); } } ================================================ FILE: examples/http-service-and-resource-paths/http_service_and_resource_paths.client.out ================================================ $ curl http://localhost:9090/info/albums [{"title":"Blue Train", "artist":"John Coltrane"}, {"title":"Jeru", "artist":"Gerry Mulligan"}] ================================================ FILE: examples/http-service-and-resource-paths/http_service_and_resource_paths.md ================================================ # REST service - Service and resource paths The `service path` is defined in the service declaration and the `resource path` is defined in the resource method definition. Each resource can be invoked by using the `service path`, `resource path`, and `resource accessor`. In an HTTP resource, the `resource accessor` confines the resource to a specific HTTP method. ::: code http_service_and_resource_paths.bal ::: Run the service as follows. ::: out http_service_and_resource_paths.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out http_service_and_resource_paths.client.out ::: ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [HTTP service path - Specification](/spec/http/#222-service-base-path) ================================================ FILE: examples/http-service-and-resource-paths/http_service_and_resource_paths.metatags ================================================ description: This example is on how to use the `service-path`, `resource-name`, and `accessor-name` (HTTP verb) in Ballerina to dispatch and constrain the service in a RESTful manner. keywords: ballerina, ballerina by example, bbe, http service, path, verb ================================================ FILE: examples/http-service-and-resource-paths/http_service_and_resource_paths.server.out ================================================ $ bal run service_path_and_resource_name.bal ================================================ FILE: examples/http-service-and-resource-paths/tests/http_service_and_resource_paths_test.bal ================================================ import ballerina/test; import ballerina/http; @test:Config {} function testFunc() returns error? { http:Client httpEndpoint = check new("localhost:9090"); Album[] payload = check httpEndpoint->get("/info/albums"); test:assertEquals(payload, [{title:"Blue Train",artist:"John Coltrane"},{title:"Jeru",artist:"Gerry Mulligan"}]); } ================================================ FILE: examples/http-service-basic-authentication-file-user-store/http_service_basic_authentication_file_user_store.bal ================================================ import ballerina/http; type Album readonly & record {| string title; string artist; |}; listener http:Listener securedEP = new (9090, secureSocket = { key: { certFile: "../resource/path/to/public.crt", keyFile: "../resource/path/to/private.key" } } ); // Basic authentication with the file user store can be enabled by setting // the `http:FileUserStoreConfig` configuration. // Authorization is based on scopes, which can be specified in the `scopes` field. @http:ServiceConfig { auth: [ { fileUserStoreConfig: {}, scopes: ["admin"] } ] } service / on securedEP { // The authentication and authorization configurations can be overwritten at // the resource level. Otherwise, the service-level configurations will be // applied automatically to the resource. resource function get albums() returns Album[] { return [ {title: "Blue Train", artist: "John Coltrane"}, {title: "Jeru", artist: "Gerry Mulligan"} ]; } } ================================================ FILE: examples/http-service-basic-authentication-file-user-store/http_service_basic_authentication_file_user_store.md ================================================ # HTTP service - Basic authentication file user store The `http:Service` can be secured with basic authentication and additionally, scopes can be added to enforce authorization. It validates the basic authentication token sent in the `Authorization` header against the provided configurations in the `Config.toml` file. The file stores the usernames and passwords for the authentication and the scopes for the authorization. To engage authentication, set the default values for the `fileUserStoreConfig` field and add the `Config.toml` file next to the service file. To engage authorization, set scopes to the `scopes` field. Both configurations must be given as part of the service configuration. A `401 Unauthorized` response is sent to the client when the authentication fails, and a `403 Forbidden` response is sent to the client when the authorization fails. Use this to authenticate and authorize requests based on user stores. Furthermore, the authentication and authorization configurations can be overwritten at the resource level using the `@http:ResourceConfig` annotation. ::: code http_service_basic_authentication_file_user_store.bal ::: ## Prerequisites - Populate the `Config.toml` file correctly with the user information as shown below. ::: code Config.toml ::: Run the service by executing the command below. ::: out http_service_basic_authentication_file_user_store.server.out ::: >**Tip:** You can invoke the above service via the [Basic authentication client](/learn/by-example/http-client-basic-authentication) example. ## Related links - [`http:FileUserStoreConfig` record - API documentation](https://lib.ballerina.io/ballerina/http/latest#FileUserStoreConfig) - [`auth` module - API documentation](https://lib.ballerina.io/ballerina/auth/latest/) - [HTTP service listener basic authentication file user store - Specification](/spec/http/#9111-listener---basic-auth---file-user-store) ================================================ FILE: examples/http-service-basic-authentication-file-user-store/http_service_basic_authentication_file_user_store.metatags ================================================ description: This example is on how to secure an HTTP service with Basic Authentication in Ballerina. keywords: ballerina, ballerina by example, bbe, http, auth, basic auth ================================================ FILE: examples/http-service-basic-authentication-file-user-store/http_service_basic_authentication_file_user_store.server.out ================================================ $ bal run http_service_basic_auth_file_user_store.bal ================================================ FILE: examples/http-service-basic-authentication-ldap-user-store/http_service_basic_authentication_ldap_user_store.bal ================================================ import ballerina/http; type Album readonly & record {| string title; string artist; |}; listener http:Listener securedEP = new (9090, secureSocket = { key: { certFile: "../resource/path/to/public.crt", keyFile: "../resource/path/to/private.key" } } ); // Basic authentication with the LDAP user store can be enabled by setting // the `http:LdapUserStoreConfig` configuration. // Authorization is based on scopes, which can be specified in the `scopes` field. @http:ServiceConfig { auth: [ { ldapUserStoreConfig: { domainName: "avix.lk", connectionUrl: "ldap://localhost:389", connectionName: "cn=admin,dc=avix,dc=lk", connectionPassword: "avix123", userSearchBase: "ou=Users,dc=avix,dc=lk", userEntryObjectClass: "inetOrgPerson", userNameAttribute: "uid", userNameSearchFilter: "(&(objectClass=inetOrgPerson)(uid=?))", userNameListFilter: "(objectClass=inetOrgPerson)", groupSearchBase: ["ou=Groups,dc=avix,dc=lk"], groupEntryObjectClass: "groupOfNames", groupNameAttribute: "cn", groupNameSearchFilter: "(&(objectClass=groupOfNames)(cn=?))", groupNameListFilter: "(objectClass=groupOfNames)", membershipAttribute: "member", userRolesCacheEnabled: true, connectionPoolingEnabled: false, connectionTimeout: 5, readTimeout: 60 }, scopes: ["admin"] } ] } service / on securedEP { // The authentication and authorization configurations can be overwritten at // the resource level. Otherwise, the service-level configurations will be // applied automatically to the resource. resource function get albums() returns Album[] { return [ {title: "Blue Train", artist: "John Coltrane"}, {title: "Jeru", artist: "Gerry Mulligan"} ]; } } ================================================ FILE: examples/http-service-basic-authentication-ldap-user-store/http_service_basic_authentication_ldap_user_store.md ================================================ # HTTP service - Basic authentication LDAP user store The `http:Service` can be secured with basic authentication and additionally, scopes can be added to enforce authorization. It validates the basic authentication token sent in the `Authorization` header with the LDAP server. This server stores the usernames and passwords for the authentication and the scopes for the authorization. To engage authentication, set the LDAP related configurations to the `ldapUserStoreConfig` field. To engage authorization, set scopes to the `scopes` field. Both configurations must be given as part of the service configuration. A `401 Unauthorized` response is sent to the client when the authentication fails, and a `403 Forbidden` response is sent to the client when the authorization fails. Use this to authenticate and authorize requests based on LDAP user stores. Furthermore, the authentication and authorization configurations can be overwritten at the resource level using the `@http:ResourceConfig` annotation. ::: code http_service_basic_authentication_ldap_user_store.bal ::: ## Prerequisites - Run the LDAP server. Run the service by executing the command below. ::: out http_service_basic_authentication_ldap_user_store.server.out ::: >**Tip:** You can invoke the above service via the [Basic authentication client](/learn/by-example/http-client-basic-authentication) example. ## Related links - [`http:LdapUserStoreConfig` - API documentation](https://lib.ballerina.io/ballerina/http/latest#LdapUserStoreConfig) - [`auth` module - API documentation](https://lib.ballerina.io/ballerina/auth/latest/) - [HTTP service listener basic authentication LDAP user store - Specification](/spec/http/#9112-listener---basic-auth---ldap-user-store) ================================================ FILE: examples/http-service-basic-authentication-ldap-user-store/http_service_basic_authentication_ldap_user_store.metatags ================================================ description: This example is on how to secure an HTTP service with LDAP Basic Authentication in Ballerina. keywords: ballerina, ballerina by example, bbe, http, auth, basic auth ================================================ FILE: examples/http-service-basic-authentication-ldap-user-store/http_service_basic_authentication_ldap_user_store.server.out ================================================ $ bal run http_service_basic_auth_ldap_user_store.bal ================================================ FILE: examples/http-service-cache-response/http_service_cache_response.bal ================================================ import ballerina/http; type Album readonly & record {| string title; string artist; |}; table key(title) albums = table [ {title: "Blue Train", artist: "John Coltrane"}, {title: "Jeru", artist: "Gerry Mulligan"} ]; service / on new http:Listener(9090) { // In this example, `max-age` directive is set to 15 seconds, indicating that the response // will be fresh for 15 seconds. By default, `must-revalidate` directive is true and instructs that // the cache should not serve a stale response without validating it with the origin server first. resource function get albums/[string title]() returns @http:Cache {maxAge: 15} Album|http:NotFound { return albums[title] ?: http:NOT_FOUND; } } ================================================ FILE: examples/http-service-cache-response/http_service_cache_response.md ================================================ # REST service - Send cache response The `http:Service` can cache a response associated with a request and reuse the cached response for subsequent requests. This can be achieved by adding the `http:Cache` annotation to the return type. By default, this annotation adds the `must-revalidate`, `public`, and `max-age=3600` directives to the `Cache-Control` header of the response, along with the `ETag` and `Last-Modified` headers. These default settings can be changed by adding the configurations to the annotation. Furthermore, the response is only cached when the return type is `anydata` or a subtype of `http:SuccessStatusCodeResponse`. ::: code http_service_cache_response.bal ::: Run the service by executing the following command. ::: out http_service_cache_response.server.out ::: >**Tip:** You can invoke the above service via the [Caching client](/learn/by-example/http-caching-client) example. In addition to that the [trace logs](/learn/by-example/http-trace-logs/) can be enabled to observe the in and out traffic. ## Related links - [`http:HttpCacheConfig` record - API documentation](https://lib.ballerina.io/ballerina/http/latest#HttpCacheConfig) - [`http:Cache` annotation - Specification](/spec/http/#46-cache-annotation) ================================================ FILE: examples/http-service-cache-response/http_service_cache_response.metatags ================================================ description: This example is on how to configure and perform HTTP caching in Ballerina. keywords: ballerina, ballerina by example, bbe, http, caching ================================================ FILE: examples/http-service-cache-response/http_service_cache_response.server.out ================================================ $ bal run http_service_cache_response.bal ================================================ FILE: examples/http-service-cache-response/tests/http_service_cache_response_test.bal ================================================ import ballerina/test; import ballerina/http; @test:Config {} function testFunc() returns error? { http:Client httpEndpoint = check new("localhost:9090"); http:Response response = check httpEndpoint->get("/albums/Blue Train"); test:assertEquals(response.getHeader("etag"), "cf55d38"); test:assertEquals(response.getHeader("cache-control"), "must-revalidate,public,max-age=15"); test:assertFalse(response.hasHeader("age")); test:assertEquals(response.getJsonPayload(), {title:"Blue Train", artist:"John Coltrane"}); response = check httpEndpoint->get("/albums/Blue Train"); test:assertEquals(response.getHeader("etag"), "cf55d38"); test:assertEquals(response.getHeader("cache-control"), "must-revalidate,public,max-age=15"); test:assertTrue(response.hasHeader("age")); test:assertEquals(response.getJsonPayload(), {title:"Blue Train", artist:"John Coltrane"}); } ================================================ FILE: examples/http-service-chunking/http_service_chunking.bal ================================================ import ballerina/http; type Album readonly & record {| string title; string artist; |}; table key(title) albums = table [ {title: "Blue Train", artist: "John Coltrane"}, {title: "Jeru", artist: "Gerry Mulligan"} ]; @http:ServiceConfig { chunking: http:CHUNKING_ALWAYS } service / on new http:Listener(9090, httpVersion = http:HTTP_1_1) { resource function get albums() returns Album[] { return albums.toArray(); } } ================================================ FILE: examples/http-service-chunking/http_service_chunking.client.out ================================================ $ curl -v localhost:9090/albums > GET /albums HTTP/1.1 > Host: localhost:9090 > User-Agent: curl/7.64.1 > Accept: */* > < HTTP/1.1 200 OK < content-type: application/json < transfer-encoding: chunked < server: ballerina < date: Wed, 4 Jan 2023 21:14:48 +0530 < [{"title":"Blue Train", "artist":"John Coltrane"}, {"title":"Jeru", "artist":"Gerry Mulligan"}] ================================================ FILE: examples/http-service-chunking/http_service_chunking.md ================================================ # HTTP service - Chunking The HTTP service can be configured for chunked responses. By default, the HTTP service sends messages with the `content-length` header. If the message size is larger than the buffer size (8K), messages are chunked. Chunking can be disabled using the `@http:ServiceConfig`. The chunking behavior can be configured as `CHUNKING_AUTO`, `CHUNKING_ALWAYS`, or `CHUNKING_NEVER` only available HTTP/1.1 protocol. When the config is set to `CHUNKING_ALWAYS`, chunking happens irrespective of the response payload size. ::: code http_service_chunking.bal ::: Run the service as follows. ::: out http_service_chunking.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out http_service_chunking.client.out ::: ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [HTTP service configuration - Specification](https://ballerina.io/spec/http/#41-service-configuration) ================================================ FILE: examples/http-service-chunking/http_service_chunking.metatags ================================================ description: This example is on how to configure the chunking behavior of an HTTP service in Ballerina. keywords: ballerina, ballerina by example, bbe, http, chunked, transfer-encoding ================================================ FILE: examples/http-service-chunking/http_service_chunking.server.out ================================================ $ bal run http_service_chunking.bal ================================================ FILE: examples/http-service-chunking/tests/http_service_chunking_test.bal ================================================ import ballerina/http; import ballerina/test; @test:Config {} function testFunc() returns error? { http:Client httpEndpoint = check new("localhost:9090"); http:Response response = check httpEndpoint->get("/albums"); test:assertEquals(response.getHeader("transfer-encoding"), "chunked"); } ================================================ FILE: examples/http-service-data-binding/http_service_data_binding.bal ================================================ import ballerina/http; type Album readonly & record {| string title; string artist; |}; table key(title) albums = table []; service / on new http:Listener(9090) { // The `album` parameter in the payload annotation represents the entity body of the inbound request. resource function post albums(Album album) returns Album { albums.add(album); return album; } } ================================================ FILE: examples/http-service-data-binding/http_service_data_binding.client.out ================================================ $ curl http://localhost:9090/albums -H "Content-type:application/json" -d "{\"title\": \"Sarah Vaughan and Clifford Brown\", \"artist\": \"Sarah Vaughan\"}" {"title":"Sarah Vaughan and Clifford Brown", "artist":"Sarah Vaughan"} ================================================ FILE: examples/http-service-data-binding/http_service_data_binding.md ================================================ # REST service - Payload data binding HTTP service payload data binding allows accessing the request payload using a resource signature parameter. The resource parameter type should be a sub type of `anydata`. By default, parameters with the `map `, `array`, `tuple`, `table`, `record` and `xml` types are mapped to the payload. For other types, the `@http:Payload` annotation is required and If the signature includes more than one of the aforementioned types, the `@http:Payload` should be used to resolve the ambiguity. This behaviour is limited to the `POST`, `PUT`, `PATCH`, `DELETE`, and `DEFAULT` accessors. If the data binding process fails, the client receives a `400 Bad Request` response. This feature allows direct access to the request payload from the resource. ::: code http_service_data_binding.bal ::: Run the service as follows. ::: out http_service_data_binding.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out http_service_data_binding.client.out ::: >**Tip:** You can invoke the above service via the [Send request/Receive response client](/learn/by-example/http-client-send-request-receive-response/) example. ## Related links - [`http:Payload` annotation - API documentation](https://lib.ballerina.io/ballerina/http/latest#Payload) - [HTTP service payload parameter - Specification](/spec/http/#2344-payload-parameter) ================================================ FILE: examples/http-service-data-binding/http_service_data_binding.metatags ================================================ description: This example is on how data binding helps to access a payload through a resource signature parameter. keywords: ballerina, ballerina by example, bbe, http, data binding ================================================ FILE: examples/http-service-data-binding/http_service_data_binding.server.out ================================================ $ bal run http_data_binding.bal ================================================ FILE: examples/http-service-data-binding/tests/http_service_data_binding_test.bal ================================================ import ballerina/http; import ballerina/test; @test:Config{} function testFunc() returns error? { http:Client httpEndpoint = check new("localhost:9090"); Album lastAlbum = check httpEndpoint->post("/albums", {title:"Sarah Vaughan and Clifford Brown", artist:"Sarah Vaughan"}); test:assertEquals(lastAlbum, {title:"Sarah Vaughan and Clifford Brown", artist:"Sarah Vaughan"}); } ================================================ FILE: examples/http-service-file-upload/http_service_file_upload.bal ================================================ import ballerina/http; import ballerina/io; service / on new http:Listener(9090) { resource function post receiver(http:Request request) returns string|error { stream streamer = check request.getByteStream(); // Writes the incoming stream to a file using the `io:fileWriteBlocksFromStream` API // by providing the file location to which the content should be written. check io:fileWriteBlocksFromStream("./files/ReceivedFile.pdf", streamer); check streamer.close(); return "File Received!"; } } ================================================ FILE: examples/http-service-file-upload/http_service_file_upload.md ================================================ # HTTP service - File upload The input streaming is handled through the Ballerina `stream` type. The resource can access the byte stream of the payload using the `getByteStream` method of the `http:Request`. This is useful when handling continuous payload, file uploads, etc. ::: code http_service_file_upload.bal ::: Run the service as follows. ::: out http_service_file_upload.server.out ::: >**Tip:** You can invoke the service via the [Client file upload](/learn/by-example/http-client-file-upload) example. ## Related links - [`getByteStream()` - API documentation](https://lib.ballerina.io/ballerina/http/latest#Request#getByteStream) - [`http` module - Specification](/spec/http/#41-service-configuration) - [Binary data](/learn/by-example/binary-data/) ================================================ FILE: examples/http-service-file-upload/http_service_file_upload.metatags ================================================ description: This example demonstrates the file upload capability through Ballerina streams. keywords: ballerina, ballerina by example, bbe, http, streaming, service, file upload ================================================ FILE: examples/http-service-file-upload/http_service_file_upload.server.out ================================================ $ bal run http_service_file_upload.bal ================================================ FILE: examples/http-service-jwt-authentication/http_service_jwt_authentication.bal ================================================ import ballerina/http; type Album readonly & record {| string title; string artist; |}; listener http:Listener securedEP = new (9090, secureSocket = { key: { certFile: "../resource/path/to/public.crt", keyFile: "../resource/path/to/private.key" } } ); // The service can be secured with JWT authentication and can be authorized optionally. // JWT authentication can be enabled by setting the `http:JwtValidatorConfig` configurations. // Authorization is based on scopes. A scope maps to one or more groups. // Authorization can be enabled by setting the `string|string[]` type configurations for the `scopes` field. @http:ServiceConfig { auth: [ { jwtValidatorConfig: { issuer: "wso2", audience: "ballerina", signatureConfig: { certFile: "../resource/path/to/public.crt" }, scopeKey: "scp" }, scopes: ["admin"] } ] } service / on securedEP { // It is optional to override the authentication and authorization configurations at the resource levels. // Otherwise, the service auth configurations are applied automatically to the resources as well. resource function get albums() returns Album[] { return [ {title: "Blue Train", artist: "John Coltrane"}, {title: "Jeru", artist: "Gerry Mulligan"} ]; } } ================================================ FILE: examples/http-service-jwt-authentication/http_service_jwt_authentication.md ================================================ # HTTP service - JWT authentication The `http:Service` and resource method can be secured with JWT and additionally, scopes can be added to enforce authorization. It validates the JWT sent in the `Authorization` header against the provided configurations. Ballerina uses the concept of scopes for authorization. A resource declared in a service can be bound to one/more scope(s). The scope can be included in the JWT using a custom claim attribute. That custom claim attribute also can be configured as the `scopeKey`. In the authorization phase, the scopes of the service/resource are compared against the scope included in the JWT for at least one match between the two sets. ::: code http_service_jwt_authentication.bal ::: Run the service by executing the command below. ::: out http_service_jwt_authentication.server.out ::: >**Tip:** You can invoke the above service via the [self-signed JWT authentication client](/learn/by-example/http-client-self-signed-jwt-authentication) example. ## Related links - [`http:JwtValidatorConfig` record - API documentation](https://lib.ballerina.io/ballerina/http/latest#JwtValidatorConfig) - [`jwt` module - API documentation](https://lib.ballerina.io/ballerina/jwt/latest/) - [HTTP service JWT authentication - Specification](/spec/http/#9113-listener---jwt-auth) ================================================ FILE: examples/http-service-jwt-authentication/http_service_jwt_authentication.metatags ================================================ description: This example is on how to secure an HTTP service with JWT authentication in Ballerina. keywords: ballerina, ballerina by example, bbe, http, auth, jwt auth ================================================ FILE: examples/http-service-jwt-authentication/http_service_jwt_authentication.server.out ================================================ $ bal run http_service_jwt_auth.bal ================================================ FILE: examples/http-service-mutual-ssl/http_service_mutual_ssl.bal ================================================ import ballerina/http; type Album readonly & record {| string title; string artist; |}; // An HTTP listener can be configured to accept new connections that are secured via mutual SSL. listener http:Listener securedEP = new (9090, secureSocket = { key: { certFile: "../resource/path/to/server-public.crt", keyFile: "../resource/path/to/server-private.key" }, // Enables mutual SSL. mutualSsl: { verifyClient: http:REQUIRE, cert: "../resource/path/to/client-public.crt" } } ); service / on securedEP { resource function get albums() returns Album[] { return [ {title: "Blue Train", artist: "John Coltrane"}, {title: "Jeru", artist: "Gerry Mulligan"} ]; } } ================================================ FILE: examples/http-service-mutual-ssl/http_service_mutual_ssl.client.out ================================================ $ curl https://localhost:9090/albums --cert /path/to/client-public.crt --key /path/to/client-private.key --cacert /path/to/server-public.crt [{"title":"Blue Train", "artist":"John Coltrane"}, {"title":"Jeru", "artist":"Gerry Mulligan"}] ================================================ FILE: examples/http-service-mutual-ssl/http_service_mutual_ssl.md ================================================ # HTTP service - Mutual SSL The `http:Listener` with mutual SSL (mTLS) enabled in it allows exposing a connection secured with mutual SSL, which is a certificate-based authentication process in which two parties (the client and server) authenticate each other by verifying the digital certificates. It ensures that both parties are assured of each other's identity. The `http:Listener` secured with mutual SSL is created by providing the `secureSocket` configurations, which require the word `require` as the `verifyClient`, the server's public certificate as the `certFile`, the server's private key as the `keyFile`, and the client's certificate as the `cert`. Use this to secure the HTTP connection over mutual SSL. ::: code http_service_mutual_ssl.bal ::: Run the service by executing the command below. ::: out http_service_mutual_ssl.server.out ::: Invoke the service by executing the cURL command below. ::: out http_service_mutual_ssl.client.out ::: >**Tip:** You can invoke the above service via the [Mutual SSL/TLS client](/learn/by-example/http-client-mutual-ssl/) example. ## Related links - [`http:ListenerSecureSocket` - API documentation](https://lib.ballerina.io/ballerina/http/latest#ListenerSecureSocket) - [HTTP service mutual SSL - Secification](/spec/http/#922-listener---mutual-ssl) ================================================ FILE: examples/http-service-mutual-ssl/http_service_mutual_ssl.metatags ================================================ description: This example is on how to secure an HTTP listener with mutual SSL. keywords: ballerina, ballerina by example, bbe, http, mutual ssl, ssl protocols, ciphers ================================================ FILE: examples/http-service-mutual-ssl/http_service_mutual_ssl.server.out ================================================ $ bal run http_service_mutual_ssl.bal ================================================ FILE: examples/http-service-oauth2/http_service_oauth2.bal ================================================ import ballerina/http; type Album readonly & record {| string title; string artist; |}; listener http:Listener securedEP = new (9090, secureSocket = { key: { certFile: "../resource/path/to/public.crt", keyFile: "../resource/path/to/private.key" } } ); // The service can be secured with OAuth2 and by enforcing authorization optionally. // It can be enabled by setting the `http:OAuth2IntrospectionConfig` configurations. // Authorization is based on scopes. A scope maps to one or more groups. // Authorization can be enabled by setting the `string|string[]` type configurations for the `scopes` field. @http:ServiceConfig { auth: [ { oauth2IntrospectionConfig: { url: "https://localhost:9445/oauth2/introspect", tokenTypeHint: "access_token", scopeKey: "scp", clientConfig: { customHeaders: {"Authorization": "Basic YWRtaW46YWRtaW4="}, secureSocket: { cert: "../resource/path/to/public.crt" } } }, scopes: ["admin"] } ] } service / on securedEP { // It is optional to override the authentication and authorization configurations at the resource levels. // Otherwise, the service auth configurations are applied automatically to the resources as well. resource function get albums() returns Album[] { return [ {title: "Blue Train", artist: "John Coltrane"}, {title: "Jeru", artist: "Gerry Mulligan"} ]; } } ================================================ FILE: examples/http-service-oauth2/http_service_oauth2.md ================================================ # HTTP service - OAuth2 The `http:Service` and resource method can be secured with OAuth2 and additionally, scopes can be added to enforce fine-grained authorization. It validates the OAuth2 token sent in the `Authorization` header against the provided configurations. This calls the configured introspection endpoint to validate. Ballerina uses the concept of scopes for authorization. A resource declared in a service can be bound to one/more scope(s). The scope can be included in the introspection response using a custom claim attribute. That custom claim attribute also can be configured as the `scopeKey`. In the authorization phase, the scopes of the service/resource are compared against the scope included in the introspection response for at least one match between the two sets. ::: code http_service_oauth2.bal ::: ## Prerequisites - An STS endpoint should be up and running. Run the service by executing the command below. ::: out http_service_oauth2.server.out ::: >**Tip:** You can invoke the above service via the [OAuth2 JWT Bearer grant type client](/learn/by-example/http-client-oauth2-jwt-bearer-grant-type) example. ## Related links - [`http:OAuth2IntrospectionConfig` - API documentation](https://lib.ballerina.io/ballerina/http/latest#OAuth2IntrospectionConfig) - [`oauth2` module - API documentation](https://lib.ballerina.io/ballerina/oauth2/latest/) - [HTTP service oauth2 - Specification](/spec/http/#9114-listener---oauth2) ================================================ FILE: examples/http-service-oauth2/http_service_oauth2.metatags ================================================ description: This example is on how to secure an HTTP service with OAuth2 in Ballerina. keywords: ballerina, ballerina by example, bbe, http, auth, oauth2, introspection ================================================ FILE: examples/http-service-oauth2/http_service_oauth2.server.out ================================================ $ bal run http_service_oauth2.bal ================================================ FILE: examples/http-service-payload-constraint-validation/http_service_payload_constraint_validation.bal ================================================ import ballerina/http; import ballerina/constraint; type Album record { @constraint:String { maxLength: 5, minLength: 1 } string title; string artist; }; service / on new http:Listener(9090) { private Album[] albums = []; // The `album` parameter in the payload annotation will get validated according to the constraints added. resource function post albums(Album album) returns http:Created { self.albums.push(album); return http:CREATED; } } ================================================ FILE: examples/http-service-payload-constraint-validation/http_service_payload_constraint_validation.client.out ================================================ $ curl -v http://localhost:9090/albums -H "Content-type:application/json" -d "{\"title\": \"Sarah Vaughan and Clifford Brown\", \"artist\": \"Sarah Vaughan\"}" * Trying 127.0.0.1:9090... * Connected to localhost (127.0.0.1) port 9090 (#0) > POST /albums HTTP/1.1 > Host: localhost:9090 > User-Agent: curl/7.79.1 > Accept: */* > Content-type:application/json > Content-Length: 72 > * Mark bundle as not supporting multiuse < HTTP/1.1 400 Bad Request < content-type: text/plain < content-length: 83 < server: ballerina < date: Thu, 8 Dec 2022 11:13:19 +0530 < * Connection #0 to host localhost left intact payload validation failed: Validation failed for '$.title:maxLength' constraint(s). ================================================ FILE: examples/http-service-payload-constraint-validation/http_service_payload_constraint_validation.md ================================================ # REST service - Payload constraint validation The Ballerina `constraint` module allows adding additional constraints to the response payload. The `http` resource method uses the `constraint` module to validate the payload against the given constraints. This validation happens soon after the successful data-binding of the request payload before executing the resource method. The constraints can be added to a given data type using different annotations. If the validation fails, a `400 Bad Request` response is returned to the client with the validation error message. Use this to validate the request payload as the application receives it, which protects the server against unnecessary resource method processing and malicious payloads. ::: code http_service_payload_constraint_validation.bal ::: Run the service program by executing the following command. ::: out http_service_payload_constraint_validation.server.out ::: Invoke the service by executing the following cURL command in a new terminal. Here, an album with a lengthy title is sent to the service. ::: out http_service_payload_constraint_validation.client.out ::: >**Tip:** You can invoke the above service via the [Send request/Receive response client](/learn/by-example/http-client-send-request-receive-response/) example. ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [`http` module - Specification](/spec/http/) - [Constraint validation example](/learn/by-example/constraint-validations/) ================================================ FILE: examples/http-service-payload-constraint-validation/http_service_payload_constraint_validation.metatags ================================================ description: This example is on how constraint validation can be done to the payload of a service. keywords: ballerina, ballerina by example, bbe, http, constraint, validation ================================================ FILE: examples/http-service-payload-constraint-validation/http_service_payload_constraint_validation.server.out ================================================ $ bal run http_service_payload_constraint_validation.bal ================================================ FILE: examples/http-service-payload-constraint-validation/tests/http_service_payload_constraint_validation.bal ================================================ import ballerina/http; import ballerina/test; @test:Config{} function testFunc() returns error? { http:Client httpEndpoint = check new("http://localhost:9090"); Album|error response = httpEndpoint->post("/albums", {title:"Sarah Vaughan and Clifford Brown", artist:"Sarah Vaughan"}); if response is http:Error { test:assertEquals(response.detail()["statusCode"], 400); http:ErrorPayload errorResponse = check response.detail()["body"].ensureType(); test:assertEquals(errorResponse.message, "payload validation failed: Validation failed for '$.title:maxLength' constraint(s)."); test:assertEquals(errorResponse.status, 400); test:assertEquals(errorResponse.reason, "Bad Request"); test:assertEquals(errorResponse.path, "/albums"); test:assertEquals(errorResponse.method, "POST"); } else { test:assertFail("Expected an error"); } } ================================================ FILE: examples/http-service-redirects/http_service_redirects.bal ================================================ import ballerina/http; service / on new http:Listener(9092) { resource function get redirect() returns http:TemporaryRedirect { // Return a redirect response record with the location header. return { headers: { "Location": "http://localhost:9090/albums" } }; } } ================================================ FILE: examples/http-service-redirects/http_service_redirects.client.out ================================================ $ curl -v localhost:9092/redirect -L > GET /redirect HTTP/1.1 > Host: localhost:9092 > User-Agent: curl/7.64.1 > Accept: */* > < HTTP/1.1 307 Temporary Redirect < Location: http://localhost:9090/albums < content-length: 0 < server: ballerina < date: Wed, 11 Jan 2023 10:45:36 +0530 < * Connection #0 to host localhost left intact * Issue another request to this URL: 'http://localhost:9090/albums' * Found bundle for host localhost: 0x7feb7341b9c0 [can pipeline] * Could pipeline, but not asked to! * Trying ::1... * TCP_NODELAY set * Connected to localhost (::1) port 9090 (#1) > GET /albums HTTP/1.1 > Host: localhost:9090 > User-Agent: curl/7.64.1 > Accept: */* > < HTTP/1.1 200 OK < content-type: application/json < content-length: 95 < server: ballerina < date: Wed, 11 Jan 2023 10:45:36 +0530 < * Connection #1 to host localhost left intact [{"title":"Blue Train", "artist":"John Coltrane"}, {"title":"Jeru", "artist":"Gerry Mulligan"}] ================================================ FILE: examples/http-service-redirects/http_service_redirects.md ================================================ # HTTP service - Redirects Redirection is important to direct requests to the correct endpoints if the called one is not existing or moved. The HTTP specification provides standard status codes to notify such situation to the caller. The HTTP service responds with `redirect` status code response along with the `location` header of the new endpoint. This can be done using the `http:StatusCodeResponse` records. ::: code http_service_redirects.bal ::: ## Prerequisites - Run the HTTP service given in the [Basic REST service](/learn/by-example/http-basic-rest-service/) example. Run the service as follows. ::: out http_service_redirects.server.out ::: Invoke the service by executing the following cURL command in a new terminal. >**Tip:** You may invoke the service via [Redirect client](../http-client-redirects/) example. ::: out http_service_redirects.client.out ::: ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [HTTP service caller - specification](/spec/http/#2341-httpcaller) ================================================ FILE: examples/http-service-redirects/http_service_redirects.metatags ================================================ description: This example is on how to perform an HTTP redirect in a Ballerina HTTP service. keywords: ballerina, ballerina by example, bbe, http, redirect ================================================ FILE: examples/http-service-redirects/http_service_redirects.server.out ================================================ $ bal run http_service_redirects.bal ================================================ FILE: examples/http-service-redirects/tests/http_service_redirects_test.bal ================================================ import ballerina/test; import ballerina/http; type Album readonly & record {| string title; string artist; |}; table key(title) albums = table [ {title: "Blue Train", artist: "John Coltrane"}, {title: "Jeru", artist: "Gerry Mulligan"} ]; @test:Config {} function testFunc() returns error? { // Invoking the main function http:Client httpEndpoint = check new("localhost:9092", followRedirects = { enabled: true, maxCount: 5 }); // Send a GET request to the specified endpoint Album[] response = check httpEndpoint->get("/redirect"); test:assertEquals(response, [{"title":"Blue Train", "artist":"John Coltrane"}, {"title":"Jeru", "artist":"Gerry Mulligan"}]); } service / on new http:Listener(9090) { resource function get albums() returns Album[] { return albums.toArray(); } resource function post albums(Album album) returns Album { albums.add(album); return album; } } ================================================ FILE: examples/http-service-ssl-tls/http_service_ssl_tls.bal ================================================ import ballerina/http; type Album readonly & record {| string title; string artist; |}; listener http:Listener securedEP = new (9090, secureSocket = { key: { certFile: "../resource/path/to/public.crt", keyFile: "../resource/path/to/private.key" } } ); service / on securedEP { resource function get albums() returns Album[] { return [ {title: "Blue Train", artist: "John Coltrane"}, {title: "Jeru", artist: "Gerry Mulligan"} ]; } } ================================================ FILE: examples/http-service-ssl-tls/http_service_ssl_tls.client.out ================================================ $ curl https://localhost:9090/albums --cacert /path/to/server-public.crt [{"title":"Blue Train", "artist":"John Coltrane"}, {"title":"Jeru", "artist":"Gerry Mulligan"}] ================================================ FILE: examples/http-service-ssl-tls/http_service_ssl_tls.md ================================================ # HTTP service - SSL/TLS The `http:Listener` can be configured to communicate through HTTPS by providing a certificate file and a private key file. The certificate and the key can be provided through the `secureSocket` field of the listener configuration. Use this to secure the communication and data transfer between the server and the client. ::: code http_service_ssl_tls.bal ::: Run the service by executing the command below. ::: out http_service_ssl_tls.server.out ::: Invoke the service by executing the cURL command below. ::: out http_service_ssl_tls.client.out ::: >**Tip:** You can invoke the above service via the [SSL/TLS client](/learn/by-example/http-client-ssl-tls/) example. ## Related links - [`http:ListenerSecureSocket` record - API documentation](https://lib.ballerina.io/ballerina/http/latest#ListenerSecureSocket) - [HTTP service SSL/TLS - Specification](/spec/http/#921-listener---ssltls) ================================================ FILE: examples/http-service-ssl-tls/http_service_ssl_tls.metatags ================================================ description: This example is on how to secure an HTTP listener with SSL. keywords: ballerina, ballerina by example, bbe, http, ssl, tls ================================================ FILE: examples/http-service-ssl-tls/http_service_ssl_tls.server.out ================================================ $ bal run http_service_ssl_tls.bal ================================================ FILE: examples/http-sse-client/http_sse_client.bal ================================================ import ballerina/http; import ballerina/io; public function main() returns error? { http:Client clientEp = check new ("localhost:9090"); // Make a GET request to the "/stocks" endpoint and receive a stream of `http:SseEvent`. stream eventStream = check clientEp->/stocks; // Iterate over the stream and handle each event. check from http:SseEvent event in eventStream do { io:println("Stock price: ", event.data); }; } ================================================ FILE: examples/http-sse-client/http_sse_client.client.out ================================================ $ bal run http_sse_client.bal Stock price: 249.9963321685791 Stock price: 56.58070945739746 Stock price: 571.2127494812012 Stock price: 217.98820853233337 Stock price: 21.891758739948273 ================================================ FILE: examples/http-sse-client/http_sse_client.md ================================================ # HTTP client - Server-sent events The HTTP client supports receiving real-time data from services using server-sent events (SSE). It allows payload-binding of a stream of `http:SseEvent` when consuming SSE from a service. This payload binding fails if the content type header is not present in the response or does not have the value `text/event-stream`. ::: code http_sse_client.bal ::: ## Prerequisites - Run the HTTP service given in the [Server-sent events](/learn/by-example/http-sse-service/) example. Run the client program by executing the following command. ::: out http_sse_client.client.out ::: ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [Client action return types - Specification](/spec/http/#243-client-action-return-types) ================================================ FILE: examples/http-sse-client/http_sse_client.metatags ================================================ description: This example demonstrates how server-sent events (SSE) can be consumed using an HTTP client. keywords: ballerina, ballerina by example, bbe, http, SSE, client ================================================ FILE: examples/http-sse-service/http_sse_service.bal ================================================ import ballerina/http; import ballerina/lang.runtime; import ballerina/random; service /stocks on new http:Listener(9090) { // This resource method returns a stream of `http:SseEvent` (with stock prices) // to push real-time data to clients using server-sent events (SSE). resource function get .() returns stream { // Create a new value of type `StockPriceEventGenerator` to generate stock price events. StockPriceEventGenerator generator = new; // Return a new stream that uses the generator to produce events. return new (generator); } } // Define a stream implementor that can be used to create a stream // of `http:SseEvent`, representing stock price events. class StockPriceEventGenerator { int eventCounter = 0; public isolated function next() returns record {|http:SseEvent value;|}|error? { // If the eventCounter reaches 5, stop generating events by returning nil. if self.eventCounter == 5 { return (); } self.eventCounter += 1; runtime:sleep(1); // Generate a random stock price float stockPrice = (check random:createIntInRange(1, 1000)) * random:createDecimal(); http:SseEvent event = {data: stockPrice.toString()}; return {value: event}; } } ================================================ FILE: examples/http-sse-service/http_sse_service.client.out ================================================ $ curl -v localhost:9090/stocks * Trying [::1]:9090... * Connected to localhost (::1) port 9090 > GET /stocks/stockA HTTP/1.1 > Host: localhost:9090 > User-Agent: curl/8.4.0 > Accept: */* > < HTTP/1.1 200 OK < content-type: text/event-stream < cache-control: no-cache,public < transfer-encoding: chunked < connection: keep-alive < server: ballerina < date: Fri, 2 Aug 2024 12:02:04 +0530 < data: 76.6014928817749 data: 789.1765828132629 data: 241.89215344190598 data: 494.8120536804199 data: 234.36854779720306 * Connection #0 to host localhost left intact ================================================ FILE: examples/http-sse-service/http_sse_service.md ================================================ # HTTP service - Server-sent events Ballerina HTTP services support pushing real-time data to clients using server-sent events (SSE). A stream of type `http:SseEvent` can be returned from service resource methods. This feature automatically handles sending SSE and sets the content type to `text/event-stream` and the transfer encoding to chunked. ::: code http_sse_service.bal ::: Run the service program by executing the following command. ::: out http_sse_service.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out http_sse_service.client.out ::: >**Tip:** You can invoke the above service via the [Client server-sent events](/learn/by-example/http-sse-client/) example. ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [Resource return types - Specification](/spec/http/#235-return-types) ================================================ FILE: examples/http-sse-service/http_sse_service.metatags ================================================ description: This example demonstrates how server-sent events (SSE) can be produced using an HTTP service. keywords: ballerina, ballerina by example, bbe, http, SSE, service ================================================ FILE: examples/http-sse-service/http_sse_service.server.out ================================================ $ bal run http_sse_service.bal ================================================ FILE: examples/http-timeout/http_timeout.bal ================================================ import ballerina/http; import ballerina/io; type Album readonly & record { string title; string artist; }; public function main() returns error? { http:Client albumClient = check new ("localhost:9090", { timeout: 10 }); Album[] payload = check albumClient->/albums; io:println(payload); } ================================================ FILE: examples/http-timeout/http_timeout.md ================================================ # HTTP client - Timeout The `timeout` field is used to gracefully handle response delays that could occur due to network problems or the back-end. The client timeout is configured in the `timeout` field of the client configuration in seconds. ::: code http_timeout.bal ::: ## Prerequisites - Run the HTTP service given in the [Basic REST service](/learn/by-example/http-basic-rest-service/) example. Run the program by executing the following command. ::: out http_timeout.out ::: ## Related links - [`http` module - API documentation](https://lib.ballerina.io/ballerina/http/latest/) - [`http` module - Specification](https://ballerina.io/spec/http/) ================================================ FILE: examples/http-timeout/http_timeout.metatags ================================================ description: This example is on how to use an HTTP timeout in Ballerina. This will set a timeout for the requests to get a response and will return an error if a response is not returned within the given timeout. keywords: ballerina, ballerina by examples, bbe, http, resiliency, timeout ================================================ FILE: examples/http-timeout/http_timeout.out ================================================ $ bal run http_timeout.bal ================================================ FILE: examples/http-timeout/tests/http_timeout_test.bal ================================================ import ballerina/test; import ballerina/http; import ballerina/lang.runtime; @test:Config {} function testFunc() returns error? { http:Client backendClientEP = check new ("localhost:8080", { timeout: 10 }); string|error response = backendClientEP->get("/greeting"); if response is error { test:assertEquals(response.message(), "Idle timeout triggered before initiating inbound response"); } else { test:assertFail("Unexpected response"); } } service / on new http:Listener(8080) { resource function get greeting() returns string { runtime:sleep(15); return "Hello World!!!"; } } ================================================ FILE: examples/http-trace-logs/http_trace_logs.bal ================================================ import ballerina/http; type Album readonly & record {| string title; string artist; |}; service /info on new http:Listener(9095) { resource function get albums(http:Request req) returns Album[]|error { http:Client albumEP = check new ("localhost:9090"); Album[] albums = check albumEP->forward("/albums", req); return albums; } } ================================================ FILE: examples/http-trace-logs/http_trace_logs.client.out ================================================ $ curl http://localhost:9095/info/albums [{"title":"Blue Train", "artist":"John Coltrane"}, {"title":"Jeru", "artist":"Gerry Mulligan"}] ================================================ FILE: examples/http-trace-logs/http_trace_logs.md ================================================ # HTTP service - Trace logs Ballerina allows enabling HTTP trace logs, which can be used to monitor the HTTP traffic that goes in and out of the application. HTTP trace logs are disabled by default. Set the log level to `TRACE` using the `-Cballerina.http.traceLogConsole=true` runtime argument to enable them. ::: code http_trace_logs.bal ::: ## Prerequisites - Run the HTTP service given in the [Basic REST service](/learn/by-example/http-basic-rest-service/) example. Run the service as follows with the runtime argument to enable trace logs. ::: out http_trace_logs.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out http_trace_logs.client.out ::: Additionally, the `Config.toml` file supports further configurations for advanced use cases, such as configuring the `hostname` and `port` of a socket service to publish the trace logs and writing trace logs to a file using the `file` configuration, which allows specifying the log file location and rotation settings. ::: code Config.toml ::: ## Related links - [`http:TraceLogAdvancedConfiguration` record](https://lib.ballerina.io/ballerina/http/latest#TraceLogAdvancedConfiguration) - [HTTP service trace log - Specification](/spec/http/#823-trace-log) ================================================ FILE: examples/http-trace-logs/http_trace_logs.metatags ================================================ description: This example is on how trace logs can be used to monitor the HTTP traffic in Ballerina. keywords: ballerina, ballerina by example, bbe, http, trace logs ================================================ FILE: examples/http-trace-logs/http_trace_logs.server.out ================================================ $ bal run http_trace_logs.bal -- -Cballerina.http.traceLogConsole=true ballerina: HTTP trace log enabled # In the logs, `http.downstream` refers to the HTTP traffic that flows between the client and Ballerina. # while `http.upstream` refers to the HTTP traffic that flows between Ballerina and the backend. [2022-12-15 11:18:03,126] TRACE {http.tracelog.downstream} - [id: 0x67ba903e] REGISTERED [2022-12-15 11:18:03,157] TRACE {http.tracelog.downstream} - [id: 0x67ba903e, correlatedSource: n/a, host:/127.0.0.1:9095 - remote:/127.0.0.1:61634] ACTIVE [2022-12-15 11:18:03,187] TRACE {http.tracelog.downstream} - [id: 0x67ba903e, correlatedSource: n/a, host:/127.0.0.1:9095 - remote:/127.0.0.1:61634] INBOUND: DefaultHttpRequest(decodeResult: success, version: HTTP/1.1) GET /info/albums HTTP/1.1 Host: localhost:9095 User-Agent: curl/7.79.1 Accept: */* [2022-12-15 11:18:03,253] TRACE {http.tracelog.downstream} - [id: 0x67ba903e, correlatedSource: n/a, host:/127.0.0.1:9095 - remote:/127.0.0.1:61634] INBOUND: EmptyLastHttpContent, 0B [2022-12-15 11:18:03,255] TRACE {http.tracelog.downstream} - [id: 0x67ba903e, correlatedSource: n/a, host:/127.0.0.1:9095 - remote:/127.0.0.1:61634] READ COMPLETE [2022-12-15 11:18:03,394] TRACE {http.tracelog.upstream} - [id: 0x40eb51fd] REGISTERED [2022-12-15 11:18:03,394] TRACE {http.tracelog.upstream} - [id: 0x40eb51fd] CONNECT: localhost/127.0.0.1:9090, null [2022-12-15 11:18:03,399] TRACE {http.tracelog.upstream} - [id: 0x40eb51fd, correlatedSource: 0x67ba903e, host:/127.0.0.1:61635 - remote:localhost/127.0.0.1:9090] ACTIVE [2022-12-15 11:18:03,402] TRACE {http.tracelog.upstream} - [id: 0x40eb51fd, correlatedSource: 0x67ba903e, host:/127.0.0.1:61635 - remote:localhost/127.0.0.1:9090] OUTBOUND: DefaultHttpRequest(decodeResult: success, version: HTTP/1.1) GET /albums HTTP/1.1 User-Agent: curl/7.79.1 Accept: */* host: localhost:9090 connection: keep-alive upgrade: h2c HTTP2-Settings: AAEAABAAAAIAAAABAAN_____AAQAAP__AAUAAEAAAAYAACAA connection: HTTP2-Settings,upgrade [2022-12-15 11:18:03,408] TRACE {http.tracelog.upstream} - [id: 0x40eb51fd, correlatedSource: 0x67ba903e, host:/127.0.0.1:61635 - remote:localhost/127.0.0.1:9090] OUTBOUND: EmptyLastHttpContent, 0B [2022-12-15 11:18:03,408] TRACE {http.tracelog.upstream} - [id: 0x40eb51fd, correlatedSource: 0x67ba903e, host:/127.0.0.1:61635 - remote:localhost/127.0.0.1:9090] FLUSH [2022-12-15 11:18:03,414] TRACE {http.tracelog.upstream} - [id: 0x40eb51fd, correlatedSource: 0x67ba903e, host:/127.0.0.1:61635 - remote:localhost/127.0.0.1:9090] INBOUND: DefaultHttpResponse(decodeResult: success, version: HTTP/1.1) HTTP/1.1 101 Switching Protocols connection: upgrade upgrade: h2c [2022-12-15 11:18:03,417] TRACE {http.tracelog.upstream} - [id: 0x40eb51fd, correlatedSource: 0x67ba903e, host:/127.0.0.1:61635 - remote:localhost/127.0.0.1:9090] INBOUND: EmptyLastHttpContent, 0B [2022-12-15 11:18:03,419] TRACE {http.tracelog.upstream} - [id: 0x40eb51fd, correlatedSource: 0x67ba903e, host:/127.0.0.1:61635 - remote:localhost/127.0.0.1:9090] OUTBOUND: 24B PRI * HTTP/2.0 SM [2022-12-15 11:18:03,421] TRACE {http.tracelog.upstream} - [id: 0x40eb51fd, L:/127.0.0.1:61635 - R:localhost/127.0.0.1:9090] OUTBOUND SETTINGS: ack=false settings={MAX_HEADER_LIST_SIZE=8192} [2022-12-15 11:18:03,422] TRACE {http.tracelog.upstream} - [id: 0x40eb51fd, correlatedSource: 0x67ba903e, host:/127.0.0.1:61635 - remote:localhost/127.0.0.1:9090] OUTBOUND: 15B [2022-12-15 11:18:03,430] TRACE {http.tracelog.upstream} - [id: 0x40eb51fd, L:/127.0.0.1:61635 - R:localhost/127.0.0.1:9090] INBOUND SETTINGS: ack=false settings={MAX_HEADER_LIST_SIZE=8192} [2022-12-15 11:18:03,431] TRACE {http.tracelog.upstream} - [id: 0x40eb51fd, L:/127.0.0.1:61635 - R:localhost/127.0.0.1:9090] OUTBOUND SETTINGS: ack=true [2022-12-15 11:18:03,437] TRACE {http.tracelog.upstream} - [id: 0x40eb51fd, L:/127.0.0.1:61635 - R:localhost/127.0.0.1:9090] INBOUND HEADERS: streamId=1 headers=DefaultHttp2Headers[:status: 200, content-type: application/json, server: ballerina, date: Thu, 15 Dec 2022 11:18:03 +0530] padding=0 endStream=false [2022-12-15 11:18:03,450] TRACE {http.tracelog.upstream} - [id: 0x40eb51fd, L:/127.0.0.1:61635 - R:localhost/127.0.0.1:9090] INBOUND DATA: streamId=1 padding=0 endStream=true length=95 data=95B [{"title":"Blue Train", "artist":"John Coltrane"}, {"title":"Jeru", "artist":"Gerry Mulligan"}] [2022-12-15 11:18:03,452] TRACE {http.tracelog.upstream} - [id: 0x40eb51fd, L:/127.0.0.1:61635 - R:localhost/127.0.0.1:9090] INBOUND SETTINGS: ack=true [2022-12-15 11:18:03,516] TRACE {http.tracelog.downstream} - [id: 0x67ba903e, correlatedSource: n/a, host:/127.0.0.1:9095 - remote:/127.0.0.1:61634] OUTBOUND: DefaultFullHttpResponse(decodeResult: success, version: HTTP/1.1, content: CompositeByteBuf(ridx: 0, widx: 95, cap: 95, components=1)) HTTP/1.1 200 OK content-type: application/json content-length: 95 server: ballerina date: Thu, 15 Dec 2022 11:18:03 +0530, 95B [{"title":"Blue Train", "artist":"John Coltrane"}, {"title":"Jeru", "artist":"Gerry Mulligan"}] [2022-12-15 11:18:03,517] TRACE {http.tracelog.downstream} - [id: 0x67ba903e, correlatedSource: n/a, host:/127.0.0.1:9095 - remote:/127.0.0.1:61634] FLUSH [2022-12-15 11:18:03,519] TRACE {http.tracelog.downstream} - [id: 0x67ba903e, correlatedSource: n/a, host:/127.0.0.1:9095 - remote:/127.0.0.1:61634] READ COMPLETE [2022-12-15 11:18:03,521] TRACE {http.tracelog.downstream} - [id: 0x67ba903e, correlatedSource: n/a, host:/127.0.0.1:9095 - remote:/127.0.0.1:61634] INACTIVE [2022-12-15 11:18:03,523] TRACE {http.tracelog.downstream} - [id: 0x67ba903e, correlatedSource: n/a, host:/127.0.0.1:9095 - remote:/127.0.0.1:61634] UNREGISTERED ================================================ FILE: examples/identifiers/identifiers.bal ================================================ import ballerina/io; // You can have Unicode identifiers. function พิมพ์ชื่อ(string ชื่อ) { // Use \u{H} to specify character using Unicode code point in hex. io:println(ชื่\u{E2D}); } // Prefix reserved keywords with a single quote. string 'from = "contact@ballerina.io"; // Prefix non-identifier character with a \\. string first\ name = "Ballerina"; public function main() { พิมพ์ชื่อ("ආයුබෝවන්"); } ================================================ FILE: examples/identifiers/identifiers.md ================================================ # Identifiers Identifier syntax is similar to C. Keywords are reserved. ::: code identifiers.bal ::: ::: out identifiers.out ::: ================================================ FILE: examples/identifiers/identifiers.metatags ================================================ description: This BBE introduces identifiers in Ballerina. keywords: ballerina, ballerina by example, bbe, identifiers ================================================ FILE: examples/identifiers/identifiers.out ================================================ $ bal run identifiers.bal ආයුබෝවන් ================================================ FILE: examples/identity/identity.bal ================================================ import ballerina/io; class MyClass { int i = 0; } public function main() { MyClass obj1 = new MyClass(); MyClass obj2 = new MyClass(); // `b1` will be true. boolean b1 = (obj1 === obj1); io:println(b1); // `b2` will be false. boolean b2 = (obj1 === obj2); io:println(b2); // `b3` will be true. boolean b3 = ([1, 2, 3] == [1, 2, 3]); io:println(b3); // `b4` will be false. boolean b4 = ([1, 2, 3] === [1, 2, 3]); io:println(b4); // `b5` will be true. boolean b5 = (-0.0 == +0.0); io:println(b5); // `b6` will be false. boolean b6 = (-0.0 === +0.0); io:println(b6); } ================================================ FILE: examples/identity/identity.md ================================================ # Identity `===` and `!==` operators test for identity. Values of mutable basic types are identical if and only if they are stored in the same address. `==` and `!=` are not defined for objects. `-0.0` and `+0.0` are equal but not identical. ::: code identity.bal ::: ::: out identity.out ::: ================================================ FILE: examples/identity/identity.metatags ================================================ description: This BBE demonstrates identity in Ballerina. keywords: ballerina, ballerina by example, bbe, identity ================================================ FILE: examples/identity/identity.out ================================================ $ bal run identity.bal true false true false true false ================================================ FILE: examples/if-statement/if_statement.bal ================================================ import ballerina/io; function getGrades(int score) returns string { // Parentheses are optional in conditions. // However, curly braces are required in `if/else` statements. if 0 < score && score < 55 { return "F"; } else if 55 <= score && score < 65 { return "C"; } else if 65 <= score && score < 75 { return "B"; } else if 75 <= score && score <= 100 { return "A"; } else { return "Invalid grade"; } } public function main() { int score = 66; string grade = getGrades(score); io:println(grade); int|string newScore = 77; // The `if` statement can be used for type narrowing. if newScore is int { io:println(getGrades(newScore)); } else { io:println("Score is not an integer"); } } ================================================ FILE: examples/if-statement/if_statement.md ================================================ # If statement `if` statements are used for conditional execution. Conditions following `if` must be boolean and if the condition is true, then, the corresponding statement block is executed. If the condition of the `if` statement is false, then, one of the following `else if` blocks executes if the particular `else if` statement condition is true. If none of the conditions are true, then, the `else` block executes. ::: code if_statement.bal ::: ::: out if_statement.out ::: ## Related links - [Boolean](/learn/by-example/boolean/) ================================================ FILE: examples/if-statement/if_statement.metatags ================================================ description: This BBE demonstrates the use of `if` statements in Ballerina. keywords: ballerina, ballerina by example, bbe, boolean, operators, if, else, else if ================================================ FILE: examples/if-statement/if_statement.out ================================================ $ bal run if_statement.bal B A ================================================ FILE: examples/ignoring-return-values-and-errors/ignoring_return_values_and_errors.bal ================================================ import ballerina/io; public function main() { // Allowed only if the return value is `()`. doX(); // Allowed if the return value does not include an `error`. _ = getX(); // Use `checkpanic` if you don't want to handle an `error`. checkpanic tryX(true); checkpanic tryX(false); } function doX() { io:println("Do X"); } function getX() returns boolean { io:println("Get X"); return true; } function tryX(boolean x) returns error? { io:println("Try X"); if !x { return error("error!"); } return; } ================================================ FILE: examples/ignoring-return-values-and-errors/ignoring_return_values_and_errors.md ================================================ # Ignore return values and errors Ballerina does not allow silently ignoring return values. To ignore a return value, assign it to `_`; this is like an implicitly declared variable that cannot be referenced. When a return type includes an error, you have to do something with the error. `_` is of type any: you cannot use `_` to ignore an error. `checkpanic` is like `check`, but panics on error rather than returning. ::: code ignoring_return_values_and_errors.bal ::: ::: out ignoring_return_values_and_errors.out ::: ================================================ FILE: examples/ignoring-return-values-and-errors/ignoring_return_values_and_errors.metatags ================================================ description: This BBE demonstrates how return values and errors can be ignored in Ballerina keywords: ballerina, ballerina by example, bbe, error, ignore value ================================================ FILE: examples/ignoring-return-values-and-errors/ignoring_return_values_and_errors.out ================================================ $ bal run ignoring_return_values_and_errors.bal Do X Get X Try X Try X error: error! at ignoring_return_values_and_errors:tryX(ignoring_return_values_and_errors.bal:28) ignoring_return_values_and_errors:main(ignoring_return_values_and_errors.bal:12) ================================================ FILE: examples/immutability/immutability.bal ================================================ import ballerina/io; type Student record {| int grade; string name; map marks; |}; public function main() { // Creates an immutable `Student` value using an intersection type with `readonly`. Student & readonly student = { grade: 12, name: "John", // The applicable contextually-expected type for marks now is `map & readonly`. // Thus, the value for marks will be constructed as an immutable map. marks: { "Maths": 75, "English": 90 } }; boolean x = student["marks"] is map & readonly; io:println(x); } ================================================ FILE: examples/immutability/immutability.md ================================================ # Immutability `anydata` values can be made immutable. Simple and `string` values are inherently immutable. A structural value can be constructed as mutable or immutable. A value includes an immutable flag. The immutable flag is fixed at the time of construction. Attempting to mutate an immutable structure causes a panic at runtime. Immutability is deep: an immutable structure can only have immutable members. An immutable value is safe for concurrent access without locking. ::: code immutability.bal ::: ::: out immutability.out ::: ================================================ FILE: examples/immutability/immutability.metatags ================================================ description: This BBE demonstrates immutability in Ballerina. keywords: ballerina, ballerina by example, bbe, immutability ================================================ FILE: examples/immutability/immutability.out ================================================ $ bal run immutability.bal true ================================================ FILE: examples/in-memory-message-store/in_memory_message_store.bal ================================================ import ballerina/messaging; import ballerina/log; // Using the in-memory message store implementation final messaging:Store store = new messaging:InMemoryMessageStore(); public function main() returns error? { // Store a message check store->store("Hello, World"); // Retrieve the message messaging:Message? msg = check store->retrieve(); if msg is messaging:Message { log:printInfo("Message retrieved", payload = msg.payload, id = msg.id); // Acknowledge the message with success will remove the message from // the store check store->acknowledge(msg.id); } // Try to retrieve the message again msg = check store->retrieve(); if msg is () { log:printInfo("No messages in the store"); } } ================================================ FILE: examples/in-memory-message-store/in_memory_message_store.md ================================================ # In-Memory Message Store This example demonstrates how to use the built-in `InMemoryMessageStore` implementation provided by the `messaging` library. The `InMemoryMessageStore` is a ready-to-use implementation of the `messaging:Store` interface that's ideal for quick testing and development scenarios where you don't need persistence. ::: code in_memory_message_store.bal ::: ::: out in_memory_message_store.out ::: ## Related links - [`messaging` module - API documentation](https://lib.ballerina.io/ballerina/messaging/latest/) - [`messaging` module - Specification](https://ballerina.io/spec/messaging/) - [Message store type](/learn/by-example/message-store-type/) - [RabbitMQ message store](https://central.ballerina.io/ballerinax/rabbitmq/latest#MessageStore) ================================================ FILE: examples/in-memory-message-store/in_memory_message_store.metatags ================================================ description: This BBE demonstrates how to use the in-memory message store from the messaging library in Ballerina. keywords: ballerina, ballerina by example, bbe, messaging, store, in-memory ================================================ FILE: examples/in-memory-message-store/in_memory_message_store.out ================================================ $ bal run in_memory_message_store.bal time=2025-10-08T08:59:48.453+05:30 level=INFO module="" message="Message retrieved" payload="Hello, World" id="01f0a3f7-0aaf-15e0-a29f-2cdcb73f2889" time=2025-10-08T08:59:48.483+05:30 level=INFO module="" message="No messages in the store" ================================================ FILE: examples/included-record-parameters/included_record_parameters.bal ================================================ import ballerina/io; type Options record {| boolean verbose = false; string? outputFile = (); |}; // `foo` has a string parameter `inputFile` and an included record parameter of the // `Options` record type. function foo(string inputFile, *Options options) { io:println("Input File:", inputFile); io:println("Options:", options); } public function main() { // Call `foo()` by directly passing a value of the `Options` record type. foo("file.txt", {verbose: true}); // Pass named arguments having the same names as the fields in the `Options` record. foo("file.txt", verbose = true); } ================================================ FILE: examples/included-record-parameters/included_record_parameters.md ================================================ # Included record parameters Ballerina allows you to define functions with included record parameters using `*T` notation. The function defines records for named parameters, where all the arguments are passed within a record value. But, the caller can pass parameters by name, which are the same as the record field names. ::: code included_record_parameters.bal ::: ::: out included_record_parameters.out ::: ================================================ FILE: examples/included-record-parameters/included_record_parameters.metatags ================================================ description: This BBE demonstrates included record parameters in Ballerina. keywords: ballerina, ballerina by example, bbe, record, included record parameters, record parameters ================================================ FILE: examples/included-record-parameters/included_record_parameters.out ================================================ $ bal run included_record_parameters.bal Input File:file.txt Options:{"verbose":true,"outputFile":null} Input File:file.txt Options:{"verbose":true,"outputFile":null} ================================================ FILE: examples/index.json ================================================ [ { "title": "Hello World", "column": 0, "category": "Language concepts", "samples": [ { "name": "Hello world main", "url": "hello-world", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Hello world service", "url": "hello-world-service", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": true } ] }, { "title": "Basics", "column": 0, "category": "Language concepts", "samples": [ { "name": "Programs and modules", "url": "programs-and-modules", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Main function", "url": "main-function", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": true }, { "name": "Init function", "url": "init-function", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Variables and types", "url": "variables-and-types", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Identifiers", "url": "identifiers", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true } ] }, { "title": "Simple basic types", "column": 0, "category": "Language concepts", "samples": [ { "name": "Integers", "url": "integers", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Floating point numbers", "url": "floating-point-numbers", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Decimal type", "url": "decimal-type", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Nil", "url": "nil", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Boolean", "url": "boolean", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Byte type", "url": "byte-type", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true } ] }, { "title": "Strings", "column": 0, "category": "Language concepts", "samples": [ { "name": "Strings", "url": "strings", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true } ] }, { "title": "Operators", "column": 0, "category": "Language concepts", "samples": [ { "name": "Binary operators", "url": "binary-operators", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Unary operators", "url": "unary-operators", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true } ] }, { "title": "Conditional statements", "column": 0, "category": "Language concepts", "samples": [ { "name": "If statement", "url": "if-statement", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Match statement", "url": "match-statement", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Match guard in match statement", "url": "match-guard-in-match-statement", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Binding patterns in match statement", "url": "binding-patterns-in-match-statement", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true } ] }, { "title": "Functions", "column": 0, "category": "Language concepts", "samples": [ { "name": "Functions", "url": "functions", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Included record parameters", "url": "included-record-parameters", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Rest parameters", "url": "rest-parameters", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Default values for function parameters", "url": "default-values-for-function-parameters", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Provide function arguments by name", "url": "provide-function-arguments-by-name", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Rest arguments", "url": "rest-arguments", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Function pointers", "url": "function-pointers", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Function values", "url": "function-values", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Function types", "url": "function-types", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Anonymous function", "url": "anonymous-function", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Function closure", "url": "function-closure", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true } ] }, { "title": "Iteration", "column": 0, "category": "Language concepts", "samples": [ { "name": "Foreach statement", "url": "foreach-statement", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "While statement", "url": "while-statement", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Break statement", "url": "break-statement", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Continue statement", "url": "continue-statement", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Range function", "url": "int-range", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true } ] }, { "title": "Lang library", "column": 0, "category": "Language concepts", "samples": [ { "name": "Langlib functions", "url": "langlib-functions", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true } ] }, { "title": "Types and typing", "column": 0, "category": "Language concepts", "samples": [ { "name": "Structural typing", "url": "structural-typing", "verifyBuild": true, "verifyOutput": true, "disablePlayground": true, "isLearnByExample": true }, { "name": "Unions", "url": "unions", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Errors", "url": "error-reporting", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Anydata type", "url": "anydata-type", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Any type", "url": "any-type", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Type definitions", "url": "type-definitions", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Typedesc type", "url": "typedesc-type", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Covariance", "url": "covariance", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Type inference", "url": "type-inference", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Built-in integer subtypes", "url": "built-in-integer-subtypes", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Built-in string subtype", "url": "built-in-string-subtype", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "ensureType function", "url": "ensureType-function", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Dependent types", "url": "dependent-types", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Singleton types", "url": "singleton-types", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Stream type", "url": "stream-type", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Never type", "url": "never-type", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true } ] }, { "title": "Expression-oriented style", "column": 0, "category": "Language concepts", "samples": [ { "name": "Expression-oriented style", "url": "expression-oriented-style", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true } ] }, { "title": "Equality", "column": 0, "category": "Language concepts", "samples": [ { "name": "Expression equality", "url": "expression-equality", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true } ] }, { "title": "Lists", "column": 0, "category": "Language concepts", "samples": [ { "name": "Arrays", "url": "arrays", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Nested arrays", "url": "nested-arrays", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Tuples", "url": "tuples", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Rest type in tuples", "url": "rest-type-in-tuples", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Filler values of a list", "url": "filler-values-of-a-list", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "List subtyping", "url": "list-subtyping", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "List equality", "url": "list-equality", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Binary data", "url": "binary-data", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true } ] }, { "title": "Mappings", "column": 1, "category": "Language concepts", "samples": [ { "name": "Maps", "url": "maps", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Records", "url": "records", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Computed field key", "url": "computed-field-key", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true } ] }, { "title": "Records", "column": 1, "category": "Language concepts", "samples": [ { "name": "Optional fields", "url": "optional-fields", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Open records", "url": "open-records", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Controlling openness", "url": "controlling-openness", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Type inclusion for records", "url": "type-inclusion-for-records", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Default values for record fields", "url": "default-values-for-record-fields", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true } ] }, { "title": "Array/Map symmetry", "column": 1, "category": "Language concepts", "samples": [ { "name": "Array/Map symmetry", "url": "array-map-symmetry", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true } ] }, { "title": "Objects", "column": 1, "category": "Language concepts", "samples": [ { "name": "Object", "url": "object", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Defining classes", "url": "defining-classes", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Object constructor", "url": "object-constructor", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Object value from class definition", "url": "object-value-from-class-definition", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Visibility of object fields and methods", "url": "visibility-of-object-fields-and-methods", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Init return type", "url": "init-return-type", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Object types", "url": "object-types", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Object type inclusion", "url": "object-type-inclusion", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Distinct object types", "url": "distinct-object-types", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Object closure", "url": "object-closure", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Client class", "url": "client-class", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Start service from service class definition", "url": "start-service-from-service-class-definition", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "Binding patterns", "column": 1, "category": "Language concepts", "samples": [ { "name": "Binding patterns", "url": "binding-patterns", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Typed binding pattern", "url": "typed-binding-pattern", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Wildcard binding pattern", "url": "wildcard-binding-pattern", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "List binding patterns", "url": "list-binding-pattern", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Rest binding pattern in list binding pattern", "url": "rest-binding-pattern-in-list-binding-pattern", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Mapping binding pattern", "url": "mapping-binding-pattern", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Rest binding pattern in mapping binding pattern", "url": "rest-binding-pattern-in-mapping-binding-pattern", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Error binding pattern", "url": "error-binding-pattern", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Rest binding pattern in error binding pattern", "url": "rest-binding-pattern-in-error-binding-pattern", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Single use of typed binding patterns", "url": "single-use-of-typed-binding", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Single use of typed binding patterns with on fail clause", "url": "single-use-with-on-fail-clause", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Iterative use of typed binding patterns", "url": "iterative-use-of-typed-binding", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "List binding pattern in match statement", "url": "list-binding-pattern-in-match-statement", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Mapping binding pattern in match statement", "url": "mapping-binding-pattern-in-match-statement", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Error binding pattern in match statement", "url": "error-binding-pattern-in-match-statement", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true } ] }, { "title": "Tables", "column": 1, "category": "Language concepts", "samples": [ { "name": "Table", "url": "table", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Table types", "url": "table-types", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Multiple key fields", "url": "multiple-key-fields", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Structured keys", "url": "structured-keys", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true } ] }, { "title": "Query expressions", "column": 1, "category": "Language concepts", "samples": [ { "name": "Query expressions", "url": "query-expressions", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Sort iterable objects", "url": "sort-iterable-objects", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Let clause", "url": "let-clause", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Limit clause", "url": "limit-clause", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Join iterable objects", "url": "joining-iterable-objects", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Outer Join clause", "url": "outer-join-clause", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Query tables", "url": "querying-tables", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Create tables with a query", "url": "create-tables-with-query", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Create maps with a query", "url": "create-maps-with-query", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Create streams with a query", "url": "create-streams-with-query", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "On conflict clause", "url": "on-conflict-clause", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Advanced conflict handling", "url": "advanced-conflict-handling", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Iterate over XML with a query", "url": "iterating-over-xml-with-query", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Nested query expressions", "url": "nested-query-expressions", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Destructure records using a query", "url": "destructure-records-using-query", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Querying streams", "url": "querying-with-streams", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Aggregation", "url": "aggregation", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true } ] }, { "title": "Query actions", "column": 2, "category": "Language concepts", "samples": [ { "name": "Query actions", "url": "query-actions", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": true } ] }, { "title": "JSON", "column": 2, "category": "Language concepts", "samples": [ { "name": "JSON type", "url": "json-type", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Access JSON elements", "url": "access-json-elements", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Access optional JSON elements", "url": "access-optional-json-elements", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Match statement with maps", "url": "match-statement-with-maps", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Convert from user-defined type to JSON", "url": "converting-from-user-defined-type-to-json", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Convert from table and XML to JSON", "url": "converting-from-table-and-xml-to-json", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Convert from JSON to user-defined type", "url": "convert-from-json-to-user-defined-type", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Cast JSON to user-defined type", "url": "casting-json-to-user-defined-type", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Resource method typing", "url": "resource-method-typing", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": true }, { "name": "JSON numbers", "url": "json-numbers", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "JSON to record", "url": "json-to-record", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "JSON to record with projection", "url": "json-to-record-with-projection", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "JSONPath expressions", "url": "jsonpath-expressions", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true } ] }, { "title": "Backtick templates", "column": 2, "category": "Language concepts", "samples": [ { "name": "Raw templates", "url": "raw-templates", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "String templates", "url": "string-templates", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "XML templates", "url": "xml-templates", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true } ] }, { "title": "XML", "column": 2, "category": "Language concepts", "samples": [ { "name": "XML data model", "url": "xml-data-model", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "XML operations", "url": "xml-operations", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "XML iteration", "url": "xml-iteration", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "XML access", "url": "xml-access", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "XML mutation", "url": "xml-mutation", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "XML subtyping", "url": "xml-subtyping", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "XML navigation ", "url": "xml-navigation", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "XML templates and query", "url": "xml-templates-and-query", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "XML namespaces", "url": "xml-namespaces", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "XMLNS declarations", "url": "xmlns-declarations", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "XML to record", "url": "xml-to-record", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "XML to record with projection", "url": "xml-to-record-with-projection", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true } ] }, { "title": "Regular Expressions", "column": 2, "category": "Language concepts", "samples": [ { "name": "RegExp type", "url": "regexp-type", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "RegExp operations overview", "url": "regexp-operations-overview", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "RegExp replace operations", "url": "regexp-replace-operations", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "RegExp find operations", "url": "regexp-find-operations", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "RegExp match operations", "url": "regexp-match-operations", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true } ] }, { "title": "Metadata", "column": 2, "category": "Language concepts", "samples": [ { "name": "Documentation", "url": "documentation", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Annotations", "url": "annotations", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true } ] }, { "title": "Network interaction", "column": 2, "category": "Language concepts", "samples": [ { "name": "Consume services: client objects", "url": "consuming-services", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Failing intermittently due to GitHub rate limiting", "isLearnByExample": true }, { "name": "Provide services", "url": "providing-services", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Service cannot be stopped", "disablePlayground": true, "isLearnByExample": true }, { "name": "Module lifecycle", "url": "module-lifecycle", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": true }, { "name": "Service declaration", "url": "service-declaration", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Does not include udp client", "disablePlayground": true, "isLearnByExample": true }, { "name": "Resource methods", "url": "resource-methods", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": true }, { "name": "Hierarchical resources", "url": "hierarchical-resources", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": true }, { "name": "Resource path parameters", "url": "resource-path-parameters", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": true } ] }, { "title": "Configurability", "column": 2, "category": "Language concepts", "samples": [ { "name": "Configurable variables", "url": "configurable-variables", "verifyBuild": true, "verifyOutput": true, "disablePlayground": true, "isLearnByExample": true }, { "name": "Configure via TOML files", "url": "configuring-via-toml", "verifyBuild": true, "verifyOutput": true, "disablePlayground": true, "isLearnByExample": true }, { "name": "Configure via CLI arguments", "url": "configuring-via-cli", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Includes varying output", "disablePlayground": true, "isLearnByExample": true } ] }, { "title": "Error handling", "column": 2, "category": "Language concepts", "samples": [ { "name": "Error handling", "url": "error-handling", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Check expression", "url": "check-expression", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Error subtyping", "url": "error-subtyping", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Panics", "url": "panics", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Trap expression", "url": "trap-expression", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Type intersection for error types", "url": "error-type-intersection", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Error detail", "url": "error-detail", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Error cause", "url": "error-cause", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true } ] }, { "title": "Ignore return values and errors", "column": 2, "category": "Language concepts", "samples": [ { "name": "Ignore return values and errors", "url": "ignoring-return-values-and-errors", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true } ] }, { "title": "Mutability and identity", "column": 3, "category": "Language concepts", "samples": [ { "name": "Identity", "url": "identity", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Const and final ", "url": "const-and-final", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Enumerations", "url": "enumerations", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Immutability", "url": "immutability", "verifyBuild": true, "verifyOutput": true, "disablePlayground": true, "isLearnByExample": true } ] }, { "title": "Concurrency", "column": 3, "category": "Language concepts", "samples": [ { "name": "Asynchronous function calls", "url": "asynchronous-function-calls", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Named workers", "url": "named-workers", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Sequence diagrams", "url": "sequence-diagrams", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": true }, { "name": "Wait for workers", "url": "waiting-for-workers", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Strands", "url": "strands", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": true }, { "name": "Named worker return values", "url": "named-worker-return-values", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Alternate wait", "url": "alternate-wait", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Includes varying output", "isLearnByExample": true }, { "name": "Multiple wait", "url": "multiple-wait", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Named workers and futures", "url": "named-workers-and-futures", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Inter-worker message passing", "url": "inter-worker-message-passing", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Alternate receive", "url": "alternate-receive", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Multiple receive", "url": "multiple-receive", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Conditional send", "url": "conditional-send", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Inter-worker failure propagation", "url": "inter-worker-failure-propagation", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Named worker with on fail clause", "url": "named-worker-with-on-fail-clause", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Synchronize message passing", "url": "synchronize-message-passing", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Asynchronize message passing", "url": "asynchronize-message-passing", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Flush", "url": "flush", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Fork", "url": "fork", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": true } ] }, { "title": "Transactions", "column": 3, "category": "Language concepts", "samples": [ { "name": "Transaction statement", "url": "transaction-statement", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Check semantics", "url": "check-semantics", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Rollback", "url": "rollback", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Retry transaction statement", "url": "retry-transaction-statement", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Transactional qualifier", "url": "transactional-qualifier", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Transactional named workers", "url": "transactional-named-workers", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Commit/rollback handlers", "url": "commit-rollback-handlers", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true } ] }, { "title": "Concurrency safety", "column": 3, "category": "Language concepts", "samples": [ { "name": "Lock statement", "url": "lock-statement", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Isolated functions", "url": "isolated-functions", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Readonly type", "url": "readonly-type", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Readonly and isolated", "url": "readonly-and-isolated", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Readonly objects and classes", "url": "readonly-objects-and-classes", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Combining isolated functions and lock", "url": "combining-isolated-functions-and-lock", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Isolated variables", "url": "isolated-variables", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Isolated methods", "url": "isolated-methods", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Isolated objects", "url": "isolated-objects", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Inferring isolated", "url": "inferring-isolated", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Run strands safely on separate threads", "url": "run-strands-safely-on-separate-threads", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true } ] }, { "title": "Interface to external code", "column": 3, "category": "Language concepts", "samples": [ { "name": "Interface to external code", "url": "interface-to-external-code", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true } ] }, { "title": "Testing", "column": 3, "category": "Language concepts", "samples": [ { "name": "Assertions", "url": "testerina-assertions", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Has different naming convention", "disablePlayground": true, "isLearnByExample": false }, { "name": "Before and after test", "url": "testerina-before-and-after-test", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Has different naming convention", "disablePlayground": true, "isLearnByExample": false }, { "name": "Before and after each", "url": "testerina-before-and-after-each", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Has different naming convention", "disablePlayground": true, "isLearnByExample": false }, { "name": "Before and after groups", "url": "testerina-before-and-after-groups", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Has different naming convention", "disablePlayground": true, "isLearnByExample": false }, { "name": "Before and after suite", "url": "testerina-before-and-after-suite", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Has different naming convention", "disablePlayground": true, "isLearnByExample": false }, { "name": "Data-driven tests", "url": "testerina-data-driven-tests", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Has different naming convention", "disablePlayground": true, "isLearnByExample": false }, { "name": "Guarantee test execution order", "url": "testerina-guarantee-test-execution-order", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Has different naming convention", "disablePlayground": true, "isLearnByExample": false }, { "name": "Group tests", "url": "testerina-group-tests", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Has different naming convention", "disablePlayground": true, "isLearnByExample": false }, { "name": "Function mocking", "url": "testerina-mocking-functions", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Has different naming convention", "disablePlayground": true, "isLearnByExample": false }, { "name": "Object mocking", "url": "testerina-mocking-objects", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Has different naming convention", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "REST service", "column": 0, "category": "Network libraries", "samples": [ { "name": "Basic REST service", "url": "http-basic-rest-service", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false }, { "name": "Service and resource paths", "url": "http-service-and-resource-paths", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false }, { "name": "Payload data binding", "url": "http-service-data-binding", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false }, { "name": "Payload constraint validation", "url": "http-service-payload-constraint-validation", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false }, { "name": "Path parameter", "url": "http-path-param", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false }, { "name": "Query parameter", "url": "http-query-parameter", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false }, { "name": "Header parameter", "url": "http-header-param", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false }, { "name": "Send response", "url": "http-send-response", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false }, { "name": "Send different status codes", "url": "http-send-different-status-codes", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false }, { "name": "Send different status codes with payload", "url": "http-send-different-status-codes-with-payload", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false }, { "name": "Error handling", "url": "http-default-error-handling", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false }, { "name": "Send cache response", "url": "http-service-cache-response", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false } ] }, { "title": "HTTP client", "column": 0, "category": "Network libraries", "samples": [ { "name": "Send request/Receive response", "url": "http-client-send-request-receive-response", "verifyBuild": false, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "Payload data binding", "url": "http-client-data-binding", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "Payload constraint validation", "url": "http-client-payload-constraint-validation", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "Path parameter", "url": "http-client-path-parameter", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "Query parameter", "url": "http-client-query-parameter", "verifyBuild": false, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "Header parameter", "url": "http-client-header-parameter", "verifyBuild": false, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "Enable caching", "url": "http-caching-client", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false } ] }, { "title": "REST service security", "column": 0, "category": "Network libraries", "samples": [ { "name": "SSL/TLS", "url": "http-service-ssl-tls", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Mutual SSL", "url": "http-service-mutual-ssl", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Basic authentication file user store", "url": "http-service-basic-authentication-file-user-store", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Basic authentication LDAP user store", "url": "http-service-basic-authentication-ldap-user-store", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "JWT authentication", "url": "http-service-jwt-authentication", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "OAuth2", "url": "http-service-oauth2", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "HTTP client security", "column": 0, "category": "Network libraries", "samples": [ { "name": "SSL/TLS", "url": "http-client-ssl-tls", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "Mutual SSL", "url": "http-client-mutual-ssl", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "Basic authentication", "url": "http-client-basic-authentication", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "Bearer token authentication", "url": "http-client-bearer-token-authentication", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "Self-signed JWT authentication", "url": "http-client-self-signed-jwt-authentication", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "OAuth2 client credentials grant type", "url": "http-client-oauth2-client-credentials-grant-type", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "OAuth2 password grant type", "url": "http-client-oauth2-password-grant-type", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "OAuth2 refresh token grant type", "url": "http-client-oauth2-refresh-token-grant-type", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "OAuth2 JWT bearer grant type", "url": "http-client-oauth2-jwt-bearer-grant-type", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false } ] }, { "title": "HTTP client resiliency", "column": 0, "category": "Network libraries", "samples": [ { "name": "Timeout", "url": "http-timeout", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "Retry", "url": "http-retry", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "Circuit breaker", "url": "http-circuit-breaker", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "Load balancer", "url": "http-load-balancer", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "Failover", "url": "http-failover", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false } ] }, { "title": "HTTP service advanced", "column": 0, "category": "Network libraries", "samples": [ { "name": "Default resource", "url": "http-default-resource", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false }, { "name": "Request/Response object", "url": "http-request-response", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false }, { "name": "Caller object", "url": "http-caller", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false }, { "name": "Redirects", "url": "http-service-redirects", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false }, { "name": "CORS", "url": "http-cors", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false }, { "name": "100 continue", "url": "http-100-continue", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false }, { "name": "Matrix parameter", "url": "http-matrix-param", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false }, { "name": "Restrict by media type", "url": "http-restrict-by-media-type", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false }, { "name": "File upload", "url": "http-service-file-upload", "verifyBuild": false, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "Compression", "url": "http-compression", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false }, { "name": "Trace logs", "url": "http-trace-logs", "verifyBuild": false, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "Access logs", "url": "http-access-logs", "verifyBuild": false, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "Cookies", "url": "http-cookies-service", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false }, { "name": "Chunking", "url": "http-service-chunking", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false }, { "name": "Sending headers", "url": "http-send-header", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false }, { "name": "Response with multiparts", "url": "http-response-with-multiparts", "verifyBuild": false, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "Passthrough", "url": "http-passthrough", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "HTTP/2 to HTTP/1.1 downgrade", "url": "http-2-to-1-1-downgrade-service", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "HTTP/2 server push", "url": "http-2-0-server-push", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "Server-sent events", "url": "http-sse-service", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false } ] }, { "title": "HTTP client advanced", "column": 0, "category": "Network libraries", "samples": [ { "name": "Redirects", "url": "http-client-redirects", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "File upload", "url": "http-client-file-upload", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "Cookies", "url": "http-cookies-client", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "Chunking", "url": "http-client-chunking", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "Request with multiparts", "url": "http-request-with-multiparts", "verifyBuild": false, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "HTTP/2 to HTTP/1.1 downgrade", "url": "http-2-to-1-1-downgrade-client", "verifyBuild": false, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "HTTP/2 prior knowledge", "url": "http-2-prior-knowledge-client", "verifyBuild": false, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "HTTP/2 server push", "url": "http-2-0-client-server-push", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false }, { "name": "Server-sent events", "url": "http-sse-client", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": false } ] }, { "title": "HTTP service interceptors", "column": 0, "category": "Network libraries", "samples": [ { "name": "Request interceptor", "url": "http-request-interceptor", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false }, { "name": "Response interceptor", "url": "http-response-interceptor", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false }, { "name": "Error handling", "url": "http-error-handling", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false }, { "name": "Interceptor error handling", "url": "http-interceptor-error-handling", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false } ] }, { "title": "GraphQL service", "column": 1, "category": "Network libraries", "samples": [ { "name": "Hello world", "url": "graphql-hello-world", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Service as output object", "url": "graphql-returning-service-objects", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Record as output object", "url": "graphql-returning-record-values", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Input types", "url": "graphql-input-types", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Mutations", "url": "graphql-mutations", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Subscriptions", "url": "graphql-subscriptions", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Input objects", "url": "graphql-input-objects", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "ID scalar type", "url": "graphql-id-scalar-type", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Interfaces", "url": "graphql-interfaces", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Interfaces implementing interfaces", "url": "graphql-interfaces-implementing-interfaces", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Union types", "url": "graphql-service-union-types", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Error handling", "url": "graphql-service-error-handling", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "GraphiQL client", "url": "graphql-graphiql", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Documentation", "url": "graphql-documentation", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Directives", "url": "graphql-directives", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Hierarchical resource paths", "url": "graphql-hierarchical-resource-paths", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false } ] }, { "title": " GraphQL service advanced", "column": 1, "category": "Network libraries", "samples": [ { "name": "Context object", "url": "graphql-context", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Field object", "url": "graphql-service-field-object", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Service interceptors", "url": "graphql-service-interceptors", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Field interceptors", "url": "graphql-field-interceptors", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Interceptor configurations", "url": "graphql-interceptor-configurations", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Input constraint validation", "url": "graphql-input-constraint-validation", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "File upload", "url": "graphql-file-upload", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Needs prerequisite condition", "disablePlayground": true, "isLearnByExample": false }, { "name": "Dataloader", "url": "graphql-dataloader", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Custom prefetch methods", "url": "custom-prefetch-methods", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Operation-level caching", "url": "graphql-service-operation-level-caching", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Field-level caching", "url": "graphql-service-field-level-caching", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Cache invalidation", "url": "graphql-service-cache-invalidation", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Query Complexity", "url": "graphql-service-query-complexity", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "GraphQL client", "column": 1, "category": "Network libraries", "samples": [ { "name": "Query GraphQL endpoint", "url": "graphql-client-query-endpoint", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Handle partial response", "url": "graphql-client-handle-partial-response", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Handle error response", "url": "graphql-client-error-handling", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "GraphQL service security", "column": 1, "category": "Network libraries", "samples": [ { "name": "SSL/TLS", "url": "graphql-service-ssl-tls", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Mutual SSL", "url": "graphql-service-mutual-ssl", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Basic authentication file user store", "url": "graphql-service-basic-auth-file-user-store", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Basic authentication LDAP user store", "url": "graphql-service-basic-auth-ldap-user-store", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "JWT authentication", "url": "graphql-service-jwt-auth", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "OAuth2", "url": "graphql-service-oauth2", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "GraphQL client security", "column": 1, "category": "Network libraries", "samples": [ { "name": "SSL/TLS", "url": "graphql-client-security-ssl-tls", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Mutual SSL", "url": "graphql-client-security-mutual-ssl", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Basic authentication", "url": "graphql-client-security-basic-auth", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Self-signed JWT authentication", "url": "graphql-client-security-jwt-authentication", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "OAuth2 password grant type", "url": "graphql-client-security-oauth2-password-grant-type", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "WebSocket service", "column": 1, "category": "Network libraries", "samples": [ { "name": "Send/Receive message", "url": "websocket-basic-sample", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Service cannot be stopped", "disablePlayground": true, "isLearnByExample": false }, { "name": "Payload constraint validation", "url": "websocket-service-payload-constraint-validation", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Service cannot be stopped", "disablePlayground": true, "isLearnByExample": false }, { "name": "Error handling", "url": "websocket-service-error-handling", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Service cannot be stopped", "disablePlayground": true, "isLearnByExample": false }, { "name": "Query parameter", "url": "websocket-query-parameter", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Service cannot be stopped", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "WebSocket client", "column": 1, "category": "Network libraries", "samples": [ { "name": "Send/Receive message", "url": "websocket-client", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Payload constraint validation", "url": "websocket-client-payload-constraint-validation", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "WebSocket service security", "column": 1, "category": "Network libraries", "samples": [ { "name": "SSL/TLS", "url": "websocket-service-ssl-tls", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Mutual SSL", "url": "websocket-service-mutual-ssl", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Basic authentication file user store", "url": "websocket-service-basic-auth-file-user-store", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Basic authentication LDAP user store", "url": "websocket-service-basic-auth-ldap-user-store", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "JWT authentication", "url": "websocket-service-jwt-auth", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "OAuth2", "url": "websocket-service-oauth2", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "WebSocket client security", "column": 1, "category": "Network libraries", "samples": [ { "name": "SSL/TLS", "url": "websocket-client-ssl-tls", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Mutual SSL", "url": "websocket-client-mutual-ssl", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Basic authentication", "url": "websocket-client-basic-auth", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Bearer token authentication", "url": "websocket-client-bearer-token-auth", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Self-signed JWT authentication", "url": "websocket-client-self-signed-jwt-auth", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "OAuth2 client credentials grant type", "url": "websocket-client-oauth2-client-cred-grant-type", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "OAuth2 password grant type", "url": "websocket-client-oauth2-password-grant-type", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "OAuth2 refresh token grant type", "url": "websocket-client-oauth2-refresh-token-grant-type", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "OAuth2 JWT bearer grant type", "url": "websocket-client-oauth2-jwt-bearer-grant-type", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "WebSocket client resiliency", "column": 1, "category": "Network libraries", "samples": [ { "name": "Timeout", "url": "websocket-timeout-client", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Retry", "url": "websocket-retry-client", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "WebSub service", "column": 1, "category": "Network libraries", "samples": [ { "name": "Consume github events", "url": "websub-webhook-sample", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "Listeners and StopHandlers", "column": 2, "category": "Network libraries", "samples": [ { "name": "Dynamic listener", "url": "dynamic-listener", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "StopHandler", "url": "stop-handler", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "gRPC service", "column": 2, "category": "Network libraries", "samples": [ { "name": "Simple RPC", "url": "grpc-service-simple", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Has different naming convention", "disablePlayground": true, "isLearnByExample": false }, { "name": "Server-side streaming RPC", "url": "grpc-service-server-streaming", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Has different naming convention", "disablePlayground": true, "isLearnByExample": false }, { "name": "Client-side streaming RPC", "url": "grpc-service-client-streaming", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Has different naming convention", "disablePlayground": true, "isLearnByExample": false }, { "name": "Bidirectional streaming RPC", "url": "grpc-service-bidirectional-streaming", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Has different naming convention", "disablePlayground": true, "isLearnByExample": false }, { "name": "Send/Receive headers", "url": "grpc-service-headers", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Has different naming convention", "disablePlayground": true, "isLearnByExample": false }, { "name": "Server reflection", "url": "grpc-server-reflection", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Has different naming convention", "disablePlayground": true, "isLearnByExample": false }, { "name": "Check deadline", "url": "grpc-service-check-deadline", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Has different naming convention", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "gRPC client", "column": 2, "category": "Network libraries", "samples": [ { "name": "Simple RPC", "url": "grpc-client-simple", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Has different naming convention", "disablePlayground": true, "isLearnByExample": false }, { "name": "Server-side streaming RPC", "url": "grpc-client-server-streaming", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Has different naming convention", "disablePlayground": true, "isLearnByExample": false }, { "name": "Client-side streaming RPC", "url": "grpc-client-client-streaming", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Has different naming convention", "disablePlayground": true, "isLearnByExample": false }, { "name": "Bidirectional streaming RPC", "url": "grpc-client-bidirectional-streaming", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Has different naming convention", "disablePlayground": true, "isLearnByExample": false }, { "name": "Send/Receive headers", "url": "grpc-client-headers", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Has different naming convention", "disablePlayground": true, "isLearnByExample": false }, { "name": "Set deadline", "url": "grpc-client-set-deadline", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Has different naming convention", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "gRPC service security", "column": 2, "category": "Network libraries", "samples": [ { "name": "SSL/TLS", "url": "grpc-service-ssl-tls", "verifyBuild": false, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Mutual SSL", "url": "grpc-service-mutual-ssl", "verifyBuild": false, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Basic authentication file user store", "url": "grpc-service-basic-auth-file-user-store", "verifyBuild": false, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Basic authentication LDAP user store", "url": "grpc-service-basic-auth-ldap-user-store", "verifyBuild": false, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "JWT authentication", "url": "grpc-service-jwt-auth", "verifyBuild": false, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "OAuth2", "url": "grpc-service-oauth2", "verifyBuild": false, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "gRPC client security", "column": 2, "category": "Network libraries", "samples": [ { "name": "SSL/TLS", "url": "grpc-client-ssl-tls", "verifyBuild": false, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Mutual SSL", "url": "grpc-client-mutual-ssl", "verifyBuild": false, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Basic authentication", "url": "grpc-client-basic-auth", "verifyBuild": false, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Bearer token authentication", "url": "grpc-client-bearer-token-auth", "verifyBuild": false, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Self-signed JWT authentication", "url": "grpc-client-self-signed-jwt-auth", "verifyBuild": false, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "OAuth2 client credentials grant type", "url": "grpc-client-oauth2-client-credentials-grant-type", "verifyBuild": false, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "OAuth2 password grant type", "url": "grpc-client-oauth2-password-grant-type", "verifyBuild": false, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "OAuth2 refresh token grant type", "url": "grpc-client-oauth2-refresh-token-grant-type", "verifyBuild": false, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "OAuth2 JWT bearer grant type", "url": "grpc-client-oauth2-jwt-bearer-grant-type", "verifyBuild": false, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "NATS service", "column": 2, "category": "Network libraries", "samples": [ { "name": "Consume message", "url": "nats-basic-sub", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "Send reply to request message", "url": "nats-basic-reply", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "Constraint validations", "url": "nats-service-constraint-validation", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "Consume JetStream message", "url": "nats-jetstream-sub", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "NATS client", "column": 2, "category": "Network libraries", "samples": [ { "name": "Publish message", "url": "nats-basic-pub", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "Send request message", "url": "nats-basic-request", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "Publish message", "url": "nats-jetstream-pub", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "NATS service security", "column": 2, "category": "Network libraries", "samples": [ { "name": "SSL/TLS", "url": "nats-service-secure-connection", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "Basic authentication", "url": "nats-service-basic-auth", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "NATS client security", "column": 2, "category": "Network libraries", "samples": [ { "name": "SSL/TLS", "url": "nats-client-secure-connection", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "Basic authentication", "url": "nats-client-basic-auth", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "Kafka service", "column": 2, "category": "Network libraries", "samples": [ { "name": "Consume message", "url": "kafka-service-consume-message", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "Constraint validation", "url": "kafka-service-constraint-validation", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "Error handling", "url": "kafka-service-error-handling", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "Kafka producer", "column": 2, "category": "Network libraries", "samples": [ { "name": "Produce message", "url": "kafka-producer-produce-message", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "Kafka consumer", "column": 2, "category": "Network libraries", "samples": [ { "name": "Payload data binding", "url": "kafka-consumer-payload-data-binding", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "Consumer record data binding", "url": "kafka-consumer-consumer-record-data-binding", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "Constraint validation", "url": "kafka-consumer-constraint-validation", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "Kafka service security", "column": 2, "category": "Network libraries", "samples": [ { "name": "SSL/TLS", "url": "kafka-service-ssl", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "SASL authentication", "url": "kafka-service-sasl", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "Kafka producer security", "column": 2, "category": "Network libraries", "samples": [ { "name": "SSL/TLS", "url": "kafka-producer-ssl", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "SASL authentication", "url": "kafka-producer-sasl", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "Kafka consumer security", "column": 2, "category": "Network libraries", "samples": [ { "name": "SSL/TLS", "url": "kafka-consumer-ssl", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "SASL authentication", "url": "kafka-consumer-sasl", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "SOAP client", "column": 2, "category": "Network libraries", "samples": [ { "name": "Send/Receive", "url": "soap-client-send-receive", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "SOAP client security", "column": 2, "category": "Network libraries", "samples": [ { "name": "SSL/TLS", "url": "soap-client-security-ssl-tsl", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Inbound Security", "url": "soap-client-security-inbound-security-config", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Outbound Security", "url": "soap-client-security-outbound-security-config", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "RabbitMQ service", "column": 3, "category": "Network libraries", "samples": [ { "name": "Consume message", "url": "rabbitmq-consumer", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "Consume message with acknowledgement", "url": "rabbitmq-consumer-with-client-acknowledgement", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "Transactional consumer", "url": "rabbitmq-transaction-consumer", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "Constraint validation", "url": "rabbitmq-service-constraint-validation", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "RabbitMQ client", "column": 3, "category": "Network libraries", "samples": [ { "name": "Declare a queue", "url": "rabbitmq-queue-declare", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "Produce message", "url": "rabbitmq-producer", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "Consume message", "url": "rabbitmq-sync-consumer", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "Transactional producer", "url": "rabbitmq-transaction-producer", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "Constraint validation", "url": "rabbitmq-client-constraint-validation", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "RabbitMQ service security", "column": 3, "category": "Network libraries", "samples": [ { "name": "SSL/TLS", "url": "rabbitmq-service-secure-connection", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "Basic authentication", "url": "rabbitmq-service-basic-auth", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "RabbitMQ client security", "column": 3, "category": "Network libraries", "samples": [ { "name": "SSL/TLS", "url": "rabbitmq-client-secure-connection", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "Basic authentication", "url": "rabbitmq-client-basic-auth", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "MQTT service", "column": 3, "category": "Network libraries", "samples": [ { "name": "Subscribe to messages", "url": "mqtt-service-subscribe-message", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Requires an MQTT server", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "MQTT client", "column": 3, "category": "Network libraries", "samples": [ { "name": "Publish message", "url": "mqtt-client-publish-message", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Requires an MQTT server", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "MQTT service security", "column": 3, "category": "Network libraries", "samples": [ { "name": "SSL/TLS", "url": "mqtt-service-ssl", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Requires an MQTT server", "disablePlayground": true, "isLearnByExample": false }, { "name": "Basic authentication", "url": "mqtt-service-basic-authentication", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Requires an MQTT server", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "MQTT client security", "column": 3, "category": "Network libraries", "samples": [ { "name": "SSL/TLS", "url": "mqtt-client-ssl", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Requires an MQTT server", "disablePlayground": true, "isLearnByExample": false }, { "name": "Basic authentication", "url": "mqtt-client-basic-authentication", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Requires an MQTT server", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "LDAP client", "column": 3, "category": "Network libraries", "samples": [ { "name": "Add/Remove entries", "url": "ldap-add-remove-entry", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Search for an entry", "url": "ldap-search-entry", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "JMS service", "column": 3, "category": "Network libraries", "samples": [ { "name": "Consume message", "url": "jms-service-consume-message", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "JMS message producer", "column": 3, "category": "Network libraries", "samples": [ { "name": "Produce message", "url": "jms-producer-produce-message", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "Transactions", "url": "jms-producer-transaction", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "JMS message consumer", "column": 3, "category": "Network libraries", "samples": [ { "name": "Consume message", "url": "jms-consumer-consume-message", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "Manual acknowledgment", "url": "jms-consumer-acknowledgement", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "TCP service", "column": 3, "category": "Network libraries", "samples": [ { "name": "Send/Receive bytes", "url": "tcp-listener", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Service cannot be stopped", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "TCP client", "column": 3, "category": "Network libraries", "samples": [ { "name": "Send/Receive bytes", "url": "tcp-client", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Service not present", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "TCP service security", "column": 3, "category": "Network libraries", "samples": [ { "name": "SSL/TLS", "url": "tcp-service-ssl-tls", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "TCP client security", "column": 3, "category": "Network libraries", "samples": [ { "name": "SSL/TLS", "url": "tcp-client-ssl-tls", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "UDP service", "column": 3, "category": "Network libraries", "samples": [ { "name": "Send/Receive datagram", "url": "udp-listener", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Service cannot be stopped", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "UDP client", "column": 3, "category": "Network libraries", "samples": [ { "name": "Send/Receive datagram", "url": "udp-client", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Service cannot be reached", "disablePlayground": true, "isLearnByExample": false }, { "name": "Send/Receive datagram with connection", "url": "udp-connect-client", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Service cannot be reached", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "Email service", "column": 3, "category": "Network libraries", "samples": [ { "name": "Receive email", "url": "receive-email-using-service", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Needs prerequisite condition", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "Email client", "column": 3, "category": "Network libraries", "samples": [ { "name": "Send email", "url": "send-email", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Needs prerequisite condition", "disablePlayground": true, "isLearnByExample": false }, { "name": "Receive email", "url": "receive-email-using-client", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Needs prerequisite condition", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "Email service security", "column": 3, "category": "Network libraries", "samples": [ { "name": "SSL/TLS", "url": "email-service-ssl-tls", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Needs prerequisite condition", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "Email client security", "column": 3, "category": "Network libraries", "samples": [ { "name": "SSL/TLS", "url": "email-client-ssl-tls", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Needs prerequisite condition", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "FTP Service", "column": 3, "category": "Network libraries", "samples": [ { "name": "Receive file", "url": "ftp-service-receive-file", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Needs prerequisite condition", "disablePlayground": true, "isLearnByExample": false }, { "name": "Send file", "url": "ftp-service-send-file", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Needs prerequisite condition", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "FTP client", "column": 3, "category": "Network libraries", "samples": [ { "name": "Receive file", "url": "ftp-client-receive-file", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Needs prerequisite condition", "disablePlayground": true, "isLearnByExample": false }, { "name": "Send file", "url": "ftp-client-send-file", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Needs prerequisite condition", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "SFTP Service", "column": 3, "category": "Network libraries", "samples": [ { "name": "Receive file", "url": "sftp-service-receive-file", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Needs prerequisite condition", "disablePlayground": true, "isLearnByExample": false }, { "name": "Send file", "url": "sftp-service-send-file", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Needs prerequisite condition", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "SFTP client", "column": 3, "category": "Network libraries", "samples": [ { "name": "Receive file", "url": "sftp-client-receive-file", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Needs prerequisite condition", "disablePlayground": true, "isLearnByExample": false }, { "name": "Send file", "url": "sftp-client-send-file", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Needs prerequisite condition", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "Database access", "column": 3, "category": "Network libraries", "samples": [ { "name": "Simple query", "url": "mysql-query-operation", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "Query with one result", "url": "mysql-query-row-operation", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "Query with advanced mapping", "url": "mysql-query-column-mapping", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "DML and DDL operations", "url": "mysql-execute-operation", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "Batch execution", "url": "mysql-batch-execute-operation", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "Atomic transactions", "url": "mysql-atomic-transaction", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "Call stored procedures", "url": "mysql-call-stored-procedures", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "Change data capture", "column": 3, "category": "Network libraries", "samples": [ { "name": "Listen to database", "url": "cdc-service", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "Group events by table", "url": "cdc-advanced-service", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "Avro", "column": 0, "category": "Common libraries", "samples": [ { "name": "Serialization/Deserialization", "url": "avro-serdes", "verifyBuild": true, "verifyOutput": true, "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "IO", "column": 0, "category": "Common libraries", "samples": [ { "name": "Read/write bytes", "url": "io-bytes", "verifyBuild": true, "verifyOutput": true, "disablePlayground": true, "isLearnByExample": false }, { "name": "Read/write strings", "url": "io-strings", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": false }, { "name": "Read/write CSV", "url": "io-csv", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": false }, { "name": "Read/write CSV with data mapping", "url": "io-csv-datamapping", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": false }, { "name": "Read/write JSON", "url": "io-json", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": false }, { "name": "Read/write XML", "url": "io-xml", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": false } ] }, { "title": "Messaging", "column": 0, "category": "Common libraries", "samples": [ { "name": "Message store type", "url": "message-store-type", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": true }, { "name": "In-memory message store", "url": "in-memory-message-store", "verifyBuild": true, "verifyOutput": false, "disablePlayground": false, "isLearnByExample": true }, { "name": "Message Store Listener", "url": "message-store-listener", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": true } ] }, { "title": "Security", "column": 0, "category": "Common libraries", "samples": [ { "name": "Cryptographic operations", "url": "security-crypto", "verifyBuild": false, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false }, { "name": "JWT issue/validate", "url": "security-jwt-issue-validate", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "URL", "column": 0, "category": "Common libraries", "samples": [ { "name": "URL encode/decode operations", "url": "url-encode-decode", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": false } ] }, { "title": "Time", "column": 1, "category": "Common libraries", "samples": [ { "name": "UTC time", "url": "time-utc", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Includes varying output", "isLearnByExample": false }, { "name": "Time with zone offset", "url": "time-utc-and-civil", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Includes varying output", "isLearnByExample": false }, { "name": "Time formatting/parsing", "url": "time-formatting-and-parsing", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": false }, { "name": "Time Zone", "url": "time-zone", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false } ] }, { "title": "Cache", "column": 1, "category": "Common libraries", "samples": [ { "name": "Cache basics", "url": "cache-basics", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": false }, { "name": "Cache invalidation", "url": "cache-invalidation", "verifyBuild": true, "verifyOutput": true, "disableVerificationReason": "Intermittently failing with github api rate limiting", "isLearnByExample": false } ] }, { "title": "Log", "column": 1, "category": "Common libraries", "samples": [ { "name": "Logging", "url": "logging", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Includes varying output", "isLearnByExample": false }, { "name": "Logging with context", "url": "logging-with-context", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Includes varying output", "isLearnByExample": false }, { "name": "Error Logging", "url": "error-logging", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Includes varying output", "isLearnByExample": false }, { "name": "Configure logging", "url": "logging-configuration", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Includes varying output", "disablePlayground": true, "isLearnByExample": false }, { "name": "Log file rotation", "url": "log-file-rotation", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Includes varying output and file creation", "disablePlayground": true, "isLearnByExample": false }, { "name": "Child loggers with context", "url": "child-loggers-with-context", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Includes varying output", "disablePlayground": true, "isLearnByExample": false }, { "name": "Logger from configuration", "url": "logger-from-config", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Includes varying output", "disablePlayground": true, "isLearnByExample": false }, { "name": "Custom logger", "url": "custom-logger", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Includes varying output", "disablePlayground": true, "isLearnByExample": false }, { "name": "Sensitive data logging", "url": "sensitive-data-logging", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Includes varying output", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "EDI", "column": 1, "category": "Common libraries", "samples": [ { "name": "EDI to record conversion", "url": "edi-to-record", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes generated code", "isLearnByExample": false }, { "name": "Record to EDI conversion", "url": "record-to-edi", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes generated code", "isLearnByExample": false } ] }, { "title": "OS", "column": 1, "category": "Common libraries", "samples": [ { "name": "Environment variables", "url": "environment-variables", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Includes varying output", "isLearnByExample": false } ] }, { "title": "File", "column": 2, "category": "Common libraries", "samples": [ { "name": "File paths", "url": "filepaths", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Includes varying output", "isLearnByExample": false }, { "name": "Directories", "url": "directories", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Includes varying output", "isLearnByExample": false }, { "name": "Files", "url": "files", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Includes varying output", "isLearnByExample": false }, { "name": "Temp files and directories", "url": "temp-files-directories", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Includes varying output", "isLearnByExample": false }, { "name": "Directory listener", "url": "directory-listener", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Needs prerequisite condition", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "Random", "column": 2, "category": "Common libraries", "samples": [ { "name": "Random numbers", "url": "random-numbers", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Includes varying output", "isLearnByExample": false } ] }, { "title": "Task", "column": 2, "category": "Common libraries", "samples": [ { "name": "Schedule job recurrence by frequency", "url": "task-frequency-job-execution", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": false }, { "name": "Schedule one time job", "url": "task-one-time-job-execution", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": false }, { "name": "Manage scheduled jobs", "url": "manage-scheduled-jobs", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": false } ] }, { "title": "UUID", "column": 2, "category": "Common libraries", "samples": [ { "name": "Generate UUID", "url": "uuid-generation", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Includes varying output", "isLearnByExample": false }, { "name": "UUID operations", "url": "uuid-operations", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": false } ] }, { "title": "XSLT", "column": 3, "category": "Common libraries", "samples": [ { "name": "XSLT transformation", "url": "xslt-transformation", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": false } ] }, { "title": "XML data", "column": 3, "category": "Common libraries", "samples": [ { "name": "XML to JSON conversion", "url": "xml-to-json-conversion", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": false }, { "name": "JSON to XML conversion", "url": "xml-from-json-conversion", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": false }, { "name": "XML to record conversion", "url": "xml-to-record-conversion", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": false }, { "name": "Record to XML conversion", "url": "xml-from-record-conversion", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": false } ] }, { "title": "YAML data", "column": 3, "category": "Common libraries", "samples": [ { "name": "YAML to anydata", "url": "yaml-to-anydata", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "YAML to anydata with projection", "url": "yaml-to-anydata-with-projection", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true }, { "name": "Serialize to YAML string", "url": "anydata-to-yaml-string", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": true } ] }, { "title": "CSV data", "column": 3, "category": "Common libraries", "samples": [ { "name": "Convert CSV string to records", "url": "csv-string-to-record-array", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": true }, { "name": "Convert CSV string to arrays", "url": "csv-string-to-anydata-array", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": true }, { "name": "Parse CSV byte streams to records", "url": "csv-streams-to-record-array", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": true }, { "name": "Parse CSV lists to custom types", "url": "parse-csv-lists", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": true }, { "name": "Transform CSV records to custom types", "url": "transform-csv-records-to-custom-types", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": true }, { "name": "Handle CSV with custom configurations", "url": "csv-user-configurations", "verifyBuild": true, "verifyOutput": false, "isLearnByExample": true } ] }, { "title": "Constraint", "column": 3, "category": "Common libraries", "samples": [ { "name": "Constraint validations", "url": "constraint-validations", "verifyBuild": true, "verifyOutput": true, "isLearnByExample": false } ] }, { "title": "Direct LLM calls", "column": 0, "category": "Generative AI", "samples": [ { "name": "Direct LLM calls", "url": "direct-llm-calls", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Requires keys and the output is not fixed", "disablePlayground": true }, { "name": "Direct LLM calls with history", "url": "direct-llm-calls-with-history", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Requires keys and the output is not fixed", "disablePlayground": true }, { "name": "Direct LLM calls with multimodal input", "url": "direct-llm-calls-with-multimodal-input", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Requires keys and the output is not fixed", "disablePlayground": true } ] }, { "title": "Retrieval-augmented generation (RAG)", "column": 1, "category": "Generative AI", "samples": [ { "name": "RAG with in-memory vector store", "url": "rag-with-in-memory-vector-store", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Requires keys", "disablePlayground": true }, { "name": "RAG ingestion with external vector store", "url": "rag-ingestion-with-external-vector-store", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Requires keys", "disablePlayground": true }, { "name": "RAG query with external vector store", "url": "rag-query-with-external-vector-store", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Requires keys and the output is not fixed", "disablePlayground": true } ] }, { "title": "Model context protocol (MCP)", "column": 1, "category": "Generative AI", "samples": [ { "name": "MCP service", "url": "mcp-service", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true }, { "name": "MCP advanced service", "url": "mcp-service-advanced", "verifyBuild": true, "verifyOutput": false, "disablePlayground": true } ] }, { "title": "AI agents", "column": 2, "category": "Generative AI", "samples": [ { "name": "Agent with local tools", "url": "ai-agent-local-tools", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Requires keys and the output is not fixed", "disablePlayground": true }, { "name": "Agent with MCP integration", "url": "ai-agent-mcp-integration", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Requires keys and the output is not fixed", "disablePlayground": true }, { "name": "Agent with external endpoint integration", "url": "ai-agent-external-endpoint-integration", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Requires keys and the output is not fixed", "disablePlayground": true }, { "name": "Chat agents", "url": "chat-agents", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Requires keys and the output is not fixed", "disablePlayground": true }, { "name": "Agent with tool kits", "url": "ai-agent-tool-kit", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Requires keys and the output is not fixed", "disablePlayground": true } ] }, { "title": "Natural expressions", "column": 3, "category": "Generative AI", "samples": [ { "name": "Natural expressions", "url": "natural-expressions", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Requires keys", "disablePlayground": true } ] }, { "title": "Docker", "column": 0, "category": "Deployment", "samples": [ { "name": "Hello world", "url": "docker-hello-world", "verifyBuild": true, "verifyOutput": false, "disableVerificationReason": "Needs prerequisite condition", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "Kubernetes", "column": 1, "category": "Deployment", "samples": [ { "name": "Hello world", "url": "kubernetes-hello-world", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Needs prerequisite condition", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "AWS Lambda", "column": 2, "category": "Deployment", "samples": [ { "name": "Hello world", "url": "aws-lambda-hello-world", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Needs prerequisite condition", "disablePlayground": true, "isLearnByExample": false }, { "name": "Execution context", "url": "aws-lambda-execution-context", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Needs prerequisite condition", "disablePlayground": true, "isLearnByExample": false }, { "name": "S3 trigger", "url": "aws-lambda-s3-trigger", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Needs prerequisite condition", "disablePlayground": true, "isLearnByExample": false }, { "name": "DynamoDB trigger", "url": "aws-lambda-dynamodb-trigger", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Needs prerequisite condition", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "Azure Functions", "column": 3, "category": "Deployment", "samples": [ { "name": "Hello world", "url": "azure-functions-hello-world", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Needs prerequisite condition", "disablePlayground": true, "isLearnByExample": false }, { "name": "Timer trigger", "url": "azure-functions-timer-trigger", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Needs prerequisite condition", "disablePlayground": true, "isLearnByExample": false }, { "name": "HTTP trigger with queue", "url": "azure-functions-http-trigger-with-queue", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Needs prerequisite condition", "disablePlayground": true, "isLearnByExample": false }, { "name": "Cosmos DB trigger", "url": "azure-functions-cosmosdb-trigger", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Needs prerequisite condition", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "Query data", "column": 0, "category": "Bal persist", "samples": [ { "name": "Get all", "url": "persist-get-all", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Needs code generation", "disablePlayground": true, "isLearnByExample": false }, { "name": "Get by key", "url": "persist-get-by-key", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Needs code generation", "disablePlayground": true, "isLearnByExample": false }, { "name": "Select fields", "url": "persist-select-fields", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Needs code generation", "disablePlayground": true, "isLearnByExample": false }, { "name": "Filter and sort", "url": "persist-filtering", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Needs code generation", "disablePlayground": true, "isLearnByExample": false }, { "name": "Relation queries", "url": "persist-relation-queries", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Needs code generation", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "Write data", "column": 1, "category": "Bal persist", "samples": [ { "name": "Create/Create many", "url": "persist-create", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Needs code generation", "disablePlayground": true, "isLearnByExample": false }, { "name": "Update", "url": "persist-update", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Needs code generation", "disablePlayground": true, "isLearnByExample": false }, { "name": "Delete", "url": "persist-delete", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Needs code generation", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "Tracing", "column": 0, "category": "Observability", "samples": [ { "name": "Distributed tracing", "url": "tracing", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false } ] }, { "title": "Metrics", "column": 1, "category": "Observability", "samples": [ { "name": "Gauge-based metrics", "url": "gauge-metrics", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false }, { "name": "Counter-based metrics", "url": "counter-metrics", "verifyBuild": false, "verifyOutput": false, "disableVerificationReason": "Includes ballerinax components", "disablePlayground": true, "isLearnByExample": false } ] } ] ================================================ FILE: examples/inferring-isolated/inferring_isolated.bal ================================================ import ballerina/io; public function main() { // function's default worker, worker `A` and `B` run concurrently. worker A { // It is safe to access the parameters of `sayHello` for the // duration of the function call. string a = sayHello("John"); io:println(a); } worker B { string b = sayHello("Anne"); io:println(b); } // `sayHello` is inferred to be an `isolated` function. boolean c = sayHello is isolated function (string str) returns string; io:println(c); } function sayHello(string name) returns string { return "Hello " + name; } ================================================ FILE: examples/inferring-isolated/inferring_isolated.md ================================================ # Inferring isolated `isolated` is a complex feature, which would be a lot for an application developer to understand. A typical Ballerina application consists of a single module that imports multiple library modules. Within a single module, the compiler infers `isolated` qualifiers. An object without mutable fields is inherently `isolated`. It is the application developer's responsibility to use `lock` statements where needed. E.g., - accessing `self` in a `service` object with mutable state - accessing mutable module-level variables Compiler can inform developer where missing locks are preventing a `service` object or method from being `isolated`. ::: code inferring_isolated.bal ::: Executing the above code gives the output below. ::: out inferring_isolated.out ::: ================================================ FILE: examples/inferring-isolated/inferring_isolated.metatags ================================================ description: This BBE introduces isolated inference in Ballerina. keywords: ballerina, ballerina by example, bbe, inferring isolated ================================================ FILE: examples/inferring-isolated/inferring_isolated.out ================================================ $ bal run inferring_isolated.bal true Hello Anne Hello John ================================================ FILE: examples/init-function/init_function.bal ================================================ import ballerina/io; // Uninitialized integer variable `value`. int value; // Uninitialized final string variable `name`. final string name; function init() returns error? { // Initialize the `value` variable to 5. value = 5; // Initialize the final variable greeting to `James`. name = "James"; if value > 3 { // The initialization will fail with this error message. return error("Value should less than 3"); } } public function main() { // This will not be executed because the init function returns an error. io:println(name); } ================================================ FILE: examples/init-function/init_function.md ================================================ # Init function The `init` function will be executed as the last function called in the program initialization phase. Uninitialized module-level `final` or `non-final` variables can be initialized in this function. The `init` function must not be declared as public. Its return type must be a subtype of `error?` or `()`. It must have no parameters. ::: code init_function.bal ::: ::: out init_function.out ::: ## Related links - [Functions](/learn/by-example/functions/) ================================================ FILE: examples/init-function/init_function.metatags ================================================ description: This BBE demonstrates returning error from an init function and constructing custom errors in Ballerina. keywords: ballerina, ballerina by example, bbe, init function, error ================================================ FILE: examples/init-function/init_function.out ================================================ $ bal run init_function.bal error: Value should less than 3 ================================================ FILE: examples/init-return-type/init_return_type.bal ================================================ import ballerina/io; // Defines a `class` called `File`. class File { string path; string contents; // The `init()` method is used to initialize the `object` when creating a new `object`. function init(string p, string? c) returns error? { self.path = p; self.contents = check c.ensureType(string); return; } } public function main() returns error? { // `new` returns the newly-constructed `File` object. File f = check new File("test.txt", "Hello World"); io:println(f.contents); return; } ================================================ FILE: examples/init-return-type/init_return_type.md ================================================ # Init return type `init` function has a return type, which must be a subtype of `error?`. If `init` returns `()`, then, `new` returns the newly constructed `object`. If `init` returns an `error`, then `new` returns that `error`. If `init` does not specify a return type, then the return type defaults to `()` as usual, meaning that `new` will never return an `error`. ::: code init_return_type.bal ::: ::: out init_return_type.out ::: ================================================ FILE: examples/init-return-type/init_return_type.metatags ================================================ description: This BBE demonstrates init function in Ballerina. keywords: ballerina, ballerina by example, bbe, init, return type ================================================ FILE: examples/init-return-type/init_return_type.out ================================================ $ bal run init_return_type.bal Hello World ================================================ FILE: examples/int-range/int_range.bal ================================================ import ballerina/io; public function main() { int[] loop1 = []; // Using `int:range(0, 5, 2)` in the `foreach` statement results in the iteration over a range of integers // from 0 to 5 (excluding) with a step of 2 between each integer (i.e., 0, 2, 4). foreach int i in int:range(0, 5, 2) { loop1.push(i); } io:println(loop1); int[] loop2 = []; // A negative step also can be used to get a descending set of integers (i.e., 5, 3, 1). foreach int i in int:range(5, 0, -2) { loop2.push(i); } io:println(loop2); } ================================================ FILE: examples/int-range/int_range.md ================================================ # int:range function The `int:range` lang library function returns an iterable object, which iterates over a range of integers. This is particularly useful when creating a loop with a decrementing looping variable or increment/decrement with a step. ::: code int_range.bal ::: ::: out int_range.out ::: ## Related links - [Langlib functions](/learn/by-example/langlib-functions) - [Foreach statement](/learn/by-example/foreach-statement) ================================================ FILE: examples/int-range/int_range.metatags ================================================ description: This BBE demonstrates the `int:range` langlib function in Ballerina. keywords: ballerina, ballerina by example, bbe, foreach, loops, int:range, range ================================================ FILE: examples/int-range/int_range.out ================================================ $ bal run int_range.bal [0,2,4] [5,3,1] ================================================ FILE: examples/integers/integers.bal ================================================ import ballerina/io; public function main() { int m = 1; // Integer literals can be hexadecimal (but not octal). int n = 0xFFFF; // You can use compound assignment operations such as `+=` and `-=`. n += m; io:println(n); } ================================================ FILE: examples/integers/integers.md ================================================ # Integers The `int` type is 64-bit signed integers and supports the usual arithmetic operators: `+ - / %`. Integer overflow is a runtime error in Ballerina. ::: code integers.bal ::: ::: out integers.out ::: ================================================ FILE: examples/integers/integers.metatags ================================================ description: This BBE introduces Ballerina int type. keywords: ballerina, ballerina by example, bbe, integers, int, int type ================================================ FILE: examples/integers/integers.out ================================================ $ bal run integers.bal 65536 ================================================ FILE: examples/inter-worker-failure-propagation/inter_worker_failure_propagation.bal ================================================ import ballerina/io; int counter = 0; function demo() returns int|error { worker A returns error? { // Workers may need to call functions that can return an `error`. // Pairing up of sends and receives guarantees that each send will be received and vice-versa // provided that the worker has failed on both sending and receiving. error? res = foo(); if res is error { return res; } 42 -> function; } // Send to or receive from failed worker will propagate the failure. int x = check <- A; return x; } function foo() returns error? { if counter == 1 { return error("maximum count exceeded"); } counter += 1; } public function main() returns error? { int a = check demo(); io:println(a); int b = check demo(); io:println(b); } ================================================ FILE: examples/inter-worker-failure-propagation/inter_worker_failure_propagation.md ================================================ # Inter-worker failure propagation Workers may need to call functions that can return an `error`. Pairing up of sends and receives guarantees that each send will be received, and vice-versa, provided neither sending nor receiving worker has failed. Send to or receive from failed worker will propagate the failure. ::: code inter_worker_failure_propagation.bal ::: ::: out inter_worker_failure_propagation.out ::: ================================================ FILE: examples/inter-worker-failure-propagation/inter_worker_failure_propagation.metatags ================================================ description: This BBE demonstrates inter-worker failure propagation keywords: ballerina, ballerina by example, bbe, worker, failure ================================================ FILE: examples/inter-worker-failure-propagation/inter_worker_failure_propagation.out ================================================ $ bal run inter_worker_failure_propagation.bal 42 error: maximum count exceeded ================================================ FILE: examples/inter-worker-message-passing/inter_worker_message_passing.bal ================================================ import ballerina/io; public function main() { worker A { // Use `-> W` to send a message to worker `W`. 1 -> B; 2 -> C; } worker B { // Use `<- W` to receive a message from worker `W`. int x1 = <- A; // Use `function` to refer to the function's default worker. x1 -> function; } worker C { int x2 = <- A; x2 -> function; } int y1 = <- B; int y2 = <- C; io:println(y1 + y2); } ================================================ FILE: examples/inter-worker-message-passing/inter_worker_message_passing.md ================================================ # Inter-worker message passing Use `-> W` or `<- W` to send a message to or receive a message from worker `W` (use `function` to refer to the function's default worker). The messages are copied using `clone()`. It implies immutable values are passed without a copy. Message sends and receives are paired up at compile-time. Each pair turns into a horizontal line in the sequence diagram. Easy to use and safe, but limited expressiveness. ::: code inter_worker_message_passing.bal ::: ::: out inter_worker_message_passing.out ::: ================================================ FILE: examples/inter-worker-message-passing/inter_worker_message_passing.metatags ================================================ description: This BBE demonstrates inter-worker message passing keywords: ballerina, ballerina by example, bbe, worker, message passing ================================================ FILE: examples/inter-worker-message-passing/inter_worker_message_passing.out ================================================ $ bal run inter_worker_message_passing.bal 3 ================================================ FILE: examples/interface-to-external-code/interface_to_external_code.bal ================================================ import ballerina/jballerina.java; // Returns the `out` field in the `java.lang.System` class. // `java.lang.System#out` is of the `java.io.PrintStream` type. public function getStdOut() returns handle = @java:FieldGet { name: "out", 'class: "java/lang/System" } external; // Invoke the `println` method on `java.lang.System#out`, // which accepts a `java.lang.String` parameter. public function printInternal(handle receiver, handle strValue) = @java:Method { name: "println", 'class: "java/io/PrintStream", paramTypes: ["java.lang.String"] } external; public function main() { handle stdOut = getStdOut(); handle str = java:fromString("Hello World"); printInternal(stdOut, str); } ================================================ FILE: examples/interface-to-external-code/interface_to_external_code.md ================================================ # Interface to external code Ballerina supports interfacing to external code. This can be done by using the `external` keyword, instead of implementing the function body. The implementation figures out how to map to the external implementation. As part of interfacing to an external implementation, Ballerina supports another basic type called `handle`. The `handle` type is basically an opaque handle that can be passed to and from external functions. There is no typing for `handle` and it can be added as a private member of a Ballerina class for better type safety. ::: code interface_to_external_code.bal ::: ::: out interface_to_external_code.out ::: ================================================ FILE: examples/interface-to-external-code/interface_to_external_code.metatags ================================================ description: This BBE demonstrates how to interface to external code in Ballerina. keywords: ballerina, ballerina by example, handle, external, interface to external code, java interoperability ================================================ FILE: examples/interface-to-external-code/interface_to_external_code.out ================================================ $ bal run interface_to_external_code.bal Hello World ================================================ FILE: examples/io-bytes/io_bytes.bal ================================================ import ballerina/io; public function main() returns error? { // Initializes the image paths. string imagePath = "./files/ballerina.jpg"; string imageCopyPath1 = "./files/ballerinaCopy1.jpg"; string imageCopyPath2 = "./files/ballerinaCopy2.jpg"; // Reads the file content as a byte array using the given file path. byte[] bytes = check io:fileReadBytes(imagePath); // Writes the already-read content to the given destination file. check io:fileWriteBytes(imageCopyPath1, bytes); io:println("Successfully copied the image as a byte array."); // Reads the file as a stream of blocks. The default block size is 4KB. // Here, the default size is overridden by the value 2KB. stream blockStream = check io:fileReadBlocksAsStream(imagePath, 2048); // If the file reading was successful, then, // the content will be written to the given destination file using the given stream. check io:fileWriteBlocksFromStream(imageCopyPath2, blockStream); io:println("Successfully copied the image as a stream."); } ================================================ FILE: examples/io-bytes/io_bytes.md ================================================ # Read/write bytes The Ballerina `io` library contains APIs to read/write bytes from/to a file. For more information on the underlying module, see the [`io` module](https://lib.ballerina.io/ballerina/io/latest/). ::: code io_bytes.bal ::: To run this sample, use the `bal run` command. ::: out io_bytes.out ::: ## Related links - [Binary data](/learn/by-example/binary-data/) ================================================ FILE: examples/io-bytes/io_bytes.metatags ================================================ description: BBE on how to read/write from/to a bytes file. keywords: ballerina, ballerina by examples, bbe, bytes ================================================ FILE: examples/io-bytes/io_bytes.out ================================================ # In the directory, which contains the `.bal` file, create a directory named `files`, # and add an image file named `ballerina.jpg` in it as follows. # tree . # ├── files # │ └── ballerina.jpeg # └── io_bytes.bal $ bal run io_bytes.bal Successfully copied the image as a byte array. Successfully copied the image as a stream. ================================================ FILE: examples/io-csv/io_csv.bal ================================================ import ballerina/io; public function main() returns error? { // Initializes the CSV file paths and content. string csvFilePath1 = "./files/csvFile1.csv"; string csvFilePath2 = "./files/csvFile2.csv"; string[][] csvContent = [["1", "James", "10000"], ["2", "Nathan", "150000"], ["3", "Ronald", "120000"], ["4", "Roy", "6000"], ["5", "Oliver", "1100000"]]; // Writes the given content `string[][]` to a CSV file in csvFilePath1. check io:fileWriteCsv(csvFilePath1, csvContent); // Reads the previously-saved CSV file as a `string[][]`. string[][] readCsv = check io:fileReadCsv(csvFilePath1); io:println(readCsv); // Writes the given content as a stream to a CSV file. check io:fileWriteCsvFromStream(csvFilePath2, csvContent.toStream()); // Reads the previously-saved CSV file as a stream. stream csvStream = check io:fileReadCsvAsStream(csvFilePath2); // Iterates through the stream and prints the content. check csvStream.forEach(function(string[] val) { io:println(val); }); } ================================================ FILE: examples/io-csv/io_csv.md ================================================ # Read/write CSV The Ballerina `io` library contains APIs to read/write CSV content from/to a file. For more information on the underlying module, see the [`io` module](https://lib.ballerina.io/ballerina/io/latest/). ::: code io_csv.bal ::: To run this sample, use the `bal run` command. ::: out io_csv.out ::: ================================================ FILE: examples/io-csv/io_csv.metatags ================================================ description: BBE on how to read/write from/to a CSV file. keywords: ballerina, ballerina by examples, bbe, csv, table ================================================ FILE: examples/io-csv/io_csv.out ================================================ $ bal run io_csv.bal [["1","James","10000"],["2","Nathan","150000"],["3","Ronald","120000"],["4","Roy","6000"],["5","Oliver","1100000"]] ["1","James","10000"] ["2","Nathan","150000"] ["3","Ronald","120000"] ["4","Roy","6000"] ["5","Oliver","1100000"] ================================================ FILE: examples/io-csv-datamapping/io_csv_datamapping.bal ================================================ import ballerina/io; // Defines the record to bind the data. type Employee record{ int id; string name; int salary; }; public function main() returns error? { // Initializes the CSV file path and content. string csvFilePath1 = "./files/csvFile1.csv"; string csvFilePath2 = "./files/csvFile2.csv"; Employee[] csvContent = [ {id: 1, name: "James", salary: 10000}, {id: 2, name: "Nathan", salary: 150000}, {id: 3, name: "Ronald", salary: 120000}, {id: 4, name: "Roy", salary: 6000}, {id: 5, name: "Oliver", salary: 1100000} ]; // Writes the given content `record[]` to a CSV file. check io:fileWriteCsv(csvFilePath1, csvContent); // Reads the previously-saved CSV file as a `record[]`. Employee[] readCsv = check io:fileReadCsv(csvFilePath1); io:println(readCsv); // Writes the given content as a stream to a CSV file. check io:fileWriteCsvFromStream(csvFilePath2, readCsv.toStream()); // Reads the previously-saved CSV file as a record stream. stream csvStream = check io:fileReadCsvAsStream(csvFilePath2); // Iterates through the stream and prints the records. check csvStream.forEach(function(Employee val) { io:println(val); }); } ================================================ FILE: examples/io-csv-datamapping/io_csv_datamapping.md ================================================ # Read/write CSV with data mapping The Ballerina `io` library contains APIs to read/write CSV content from/to a file and these APIs can be used to bind the CSV data with user-specified records. For more information on the underlying module, see the [`io` module](https://lib.ballerina.io/ballerina/io/latest/). ::: code io_csv_datamapping.bal ::: To run this sample, use the `bal run` command. ::: out io_csv_datamapping.out ::: ================================================ FILE: examples/io-csv-datamapping/io_csv_datamapping.metatags ================================================ description: BBE on how to read/write from/to a CSV file with data binding. keywords: ballerina, ballerina by examples, bbe, csv, table, data binding ================================================ FILE: examples/io-csv-datamapping/io_csv_datamapping.out ================================================ $ bal run io_csv_datamapping.bal [{"id":1,"name":"James","salary":10000},{"id":2,"name":"Nathan","salary":150000},{"id":3,"name":"Ronald","salary":120000},{"id":4,"name":"Roy","salary":6000},{"id":5,"name":"Oliver","salary":1100000}] {"id":1,"name":"James","salary":10000} {"id":2,"name":"Nathan","salary":150000} {"id":3,"name":"Ronald","salary":120000} {"id":4,"name":"Roy","salary":6000} {"id":5,"name":"Oliver","salary":1100000} ================================================ FILE: examples/io-json/io_json.bal ================================================ import ballerina/io; public function main() returns error? { // Initializes the JSON file path and content. string jsonFilePath = "./files/jsonFile.json"; json jsonContent = {"Store": { "@id": "AST", "name": "Anne", "address": { "street": "Main", "city": "94" }, "codes": ["4", "8"] }}; // Writes the given JSON to a file. check io:fileWriteJson(jsonFilePath, jsonContent); // If the write operation was successful, then, // performs a read operation to read the JSON content. json readJson = check io:fileReadJson(jsonFilePath); io:println(readJson); } ================================================ FILE: examples/io-json/io_json.md ================================================ # Read/write JSON The Ballerina `io` library contains APIs to read/write JSON objects from/to a file. For more information on the underlying module, see the [`io` module](https://lib.ballerina.io/ballerina/io/latest/). ::: code io_json.bal ::: To run this sample, use the `bal run` command. ::: out io_json.out ::: ================================================ FILE: examples/io-json/io_json.metatags ================================================ description: BBE on how to read/write from/to a JSON file. keywords: ballerina, ballerina by examples, bbe, json ================================================ FILE: examples/io-json/io_json.out ================================================ $ bal run io_json.bal {"Store":{"@id":"AST","name":"Anne","address":{"street":"Main","city":"94"},"codes":["4","8"]}} ================================================ FILE: examples/io-strings/io_strings.bal ================================================ import ballerina/io; public function main() returns error? { // Initializes the text path and the content. string textFilePath1 = "./files/textfile1.txt"; string textFilePath2 = "./files/textfile2.txt"; string textFilePath3 = "./files/textfile3.txt"; string textContent = "Ballerina is an open source programming language."; string[] lines = ["The Big Bang Theory", "F.R.I.E.N.D.S", "Game of Thrones", "LOST"]; // Writes the given string to a file. check io:fileWriteString(textFilePath1, textContent); // If the write operation was successful, then, reads the content as a string. string readContent = check io:fileReadString(textFilePath1); io:println(readContent); // Writes the given array of lines to a file. check io:fileWriteLines(textFilePath2, lines); // If the write operation was successful, then, // performs a read operation to read the lines as an array. string[] readLines = check io:fileReadLines(textFilePath2); io:println(readLines); // Writes the given stream of lines to a file. check io:fileWriteLinesFromStream(textFilePath3, lines.toStream()); // If the write operation was successful, then, // performs a read operation to read the lines as a stream. stream lineStream = check io:fileReadLinesAsStream(textFilePath3); // Iterates through the stream and prints the content. check lineStream.forEach(function(string val) { io:println(val); }); } ================================================ FILE: examples/io-strings/io_strings.md ================================================ # Read/write strings The Ballerina `io` library contains APIs to read/write text content from/to a file. For more information on the underlying module, see the [`io` module](https://lib.ballerina.io/ballerina/io/latest/). ::: code io_strings.bal ::: To run this sample, use the `bal run` command. ::: out io_strings.out ::: ================================================ FILE: examples/io-strings/io_strings.metatags ================================================ description: BBE on how to read/write from/to a text file. keywords: ballerina, ballerina by examples, bbe, text, strings ================================================ FILE: examples/io-strings/io_strings.out ================================================ $ bal run io_strings.bal Ballerina is an open source programming language. ["The Big Bang Theory","F.R.I.E.N.D.S","Game of Thrones","LOST"] The Big Bang Theory F.R.I.E.N.D.S Game of Thrones LOST ================================================ FILE: examples/io-xml/io_xml.bal ================================================ import ballerina/io; public function main() returns error? { // Initializes the XML file path and content. string xmlFilePath = "./files/xmlFile.xml"; xml xmlContent = xml `The Lost World`; // Writes the given XML to a file. check io:fileWriteXml(xmlFilePath, xmlContent); // If the write operation was successful, then, // performs a read operation to read the XML content. xml readXml = check io:fileReadXml(xmlFilePath); io:println(readXml); } ================================================ FILE: examples/io-xml/io_xml.md ================================================ # Read/write XML The Ballerina `io` library contains APIs to read/write XML content from/to a file. For more information on the underlying module, see the [`io` module](https://lib.ballerina.io/ballerina/io/latest/). ::: code io_xml.bal ::: To run this sample, use the `bal run` command. ::: out io_xml.out ::: ================================================ FILE: examples/io-xml/io_xml.metatags ================================================ description: BBE on how to read/write from/to an XML file. keywords: ballerina, ballerina by examples, bbe, xml ================================================ FILE: examples/io-xml/io_xml.out ================================================ $ bal run io_xml.bal The Lost World ================================================ FILE: examples/isolated-functions/isolated_functions.bal ================================================ import ballerina/io; type R record { int v; }; final int N = getN(); function getN() returns int { return 100; } // Can access mutable state that is passed as a parameter. isolated function set(R r) returns R { // Can access non-`isolated` module-level state only if the variable // is `final` and the type is a subtype of `readonly` or // `isolated object {}`. r.v = N; return r; } R r = {v: 0}; // This is not an `isolated` function. function setGlobal(int n) { r.v = n; } public function main() { setGlobal(200); io:println(r); io:println(set(r)); } ================================================ FILE: examples/isolated-functions/isolated_functions.md ================================================ # Isolated functions A call to an `isolated` function is concurrency-safe if it is called with arguments that are safe at least until the call returns. A function defined as `isolated`: - has access to mutable state only through its parameters - has unrestricted access to immutable state - can only call functions that are `isolated` Constraints are enforced at compile-time. `isolated` is a part of the function type. Weaker concept than pure function. ::: code isolated_functions.bal ::: By executing the above code, it can be seen that the value of `v` is changed to 100 by calling `set` function. ::: out isolated_functions.out ::: ================================================ FILE: examples/isolated-functions/isolated_functions.metatags ================================================ description: This BBE introduces isolated functions in Ballerina. keywords: ballerina, ballerina by example, bbe, isolated functions, concurrency safety ================================================ FILE: examples/isolated-functions/isolated_functions.out ================================================ $ bal run isolated_functions.bal {"v":200} {"v":100} ================================================ FILE: examples/isolated-methods/isolated_methods.bal ================================================ import ballerina/io; class EvenNumber { int i = 1; // `isolated` method. isolated function generate() returns int { lock { // Uses `self` to access mutable field `i` // within a `lock` statement. return self.i * 2; } } } public function main() { EvenNumber e = new; int c = e.generate(); io:println(c); } ================================================ FILE: examples/isolated-methods/isolated_methods.md ================================================ # Isolated methods Object methods can be `isolated`. An `isolated` method is same as an `isolated` function with `self` treated as a parameter. An `isolated` method call is concurrency-safe if both the object is safe and the arguments are safe. This is not quite enough for service concurrency. When a `listener` makes calls to a `remote` or `resource` method, - it can ensure the safety of arguments it passes - it has no way to ensure the safety of the object itself (since the object may have fields) ::: code isolated_methods.bal ::: Executing the above code gives the output below. ::: out isolated_methods.out ::: ================================================ FILE: examples/isolated-methods/isolated_methods.metatags ================================================ description: This BBE introduces isolated methods in Ballerina. keywords: ballerina, ballerina by example, bbe, isolated methods ================================================ FILE: examples/isolated-methods/isolated_methods.out ================================================ $ bal run isolated_methods.bal 2 ================================================ FILE: examples/isolated-objects/isolated_objects.bal ================================================ import ballerina/io; // An `isolated` object’s mutable state is `isolated` from the // rest of the program. isolated class Counter { // `n` is a mutable field. private int n = 0; isolated function get() returns int { lock { // `n` can only be accessed using `self`. return self.n; } } isolated function inc() { lock { self.n += 1; } } } public function main() { // The object’s mutable state is accessible only via the // object itself making it an “isolated root”. Counter c = new; c.inc(); int v = c.get(); io:println(v); } ================================================ FILE: examples/isolated-objects/isolated_objects.md ================================================ # Isolated objects An object defined as `isolated` is similar to a module with `isolated` module-level variables. Mutable fields of an `isolated` object, - must be `private` and so can only be accessed using `self` - must be initialized with an `isolated` expression - must only be accessed within a `lock` statement - `lock` statement must follow the same rules for `self` as for an `isolated` variable - a field is mutable unless it is `final` and has a type that is a subtype of `readonly` Isolated root concept treats `isolated` objects as opaque. Isolated functions can access a `final` variable whose type is an `isolated` object. ::: code isolated_objects.bal ::: Executing the above code gives the output below. ::: out isolated_objects.out ::: ================================================ FILE: examples/isolated-objects/isolated_objects.metatags ================================================ description: This BBE introduces isolated objects in Ballerina. keywords: ballerina, ballerina by example, bbe, isolated objects ================================================ FILE: examples/isolated-objects/isolated_objects.out ================================================ $ bal run isolated_objects.bal 1 ================================================ FILE: examples/isolated-variables/isolated_variables.bal ================================================ import ballerina/io; // Initializes an `isolated` variable using // an `isolated` expression. isolated int[] stack = []; isolated function push(int n) { // Accesses `isolated` variable within a // `lock` statement. lock { stack.push(n); } } isolated function pop() returns int { lock { return stack.pop(); } } public function main() { push(10); push(20); io:println(pop()); } ================================================ FILE: examples/isolated-variables/isolated_variables.md ================================================ # Isolated variables When a variable is declared as `isolated`, the compiler guarantees that it is an `isolated` root and accessed only within a `lock` statement. An isolated variable declaration must be a non-public module-level variable declaration initialized with an `isolated` expression. A `lock` statement that accesses an `isolated` variable must maintain `isolated` root invariant: - access only one `isolated` variable - call only `isolated` functions - transfers of values in and out must use `isolated` expressions The `isolated` functions are allowed to access `isolated` module-level variables, provided they follow the above rules. ::: code isolated_variables.bal ::: Executing the above code gives the output below. ::: out isolated_variables.out ::: ================================================ FILE: examples/isolated-variables/isolated_variables.metatags ================================================ description: This BBE introduces isolated variables in Ballerina. keywords: ballerina, ballerina by example, bbe, isolated variables ================================================ FILE: examples/isolated-variables/isolated_variables.out ================================================ $ bal run isolated_variables.bal 20 ================================================ FILE: examples/iterating-over-xml-with-query/iterating_over_xml_with_query.bal ================================================ import ballerina/io; type Book record {| string id; string name; |}; public function main() { xml categories = xml ` 1 cooking 2 children 3 fantasy `; // Iterates through the `categories` XML sequence, constructs the `Book` records, // and collects them into a table. table books = from var category in categories/ select { id: (category/).toString(), name: (category/).toString() }; io:println(books); } ================================================ FILE: examples/iterating-over-xml-with-query/iterating_over_xml_with_query.md ================================================ # Iterate over XML with query An `XML` value can be iterated by using a query expression. A query expression will iterate over each sequence item. ::: code iterating_over_xml_with_query.bal ::: ::: out iterating_over_xml_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) - [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/iterating-over-xml-with-query/iterating_over_xml_with_query.metatags ================================================ description: This BBE demonstrates iterating XML values, creating a table from XML, and filtering XML values. keywords: ballerina, ballerina by example, bbe, xml, filter, iterate, query ================================================ FILE: examples/iterating-over-xml-with-query/iterating_over_xml_with_query.out ================================================ $ bal run iterating_over_xml_with_query.bal [{"id":"1","name":"cooking"},{"id":"2","name":"children"},{"id":"3","name":"fantasy"}] ================================================ FILE: examples/iterative-use-of-typed-binding/iterative_use_of_typed_binding.bal ================================================ import ballerina/io; type Person record {| int id; string fname; string lname; |}; public function main() { int sum = 0; string names = ""; [string, int][] personInfoList = getPersonInfo(); Person[] personList = getPersonList(); // The values in the `personInfoList` is bound to the `[name, age]` list binding for each iteration. foreach [string, int] [name, age] in personInfoList { names += " " + name; sum += age; } io:println("Average age of", names, ": ", sum / 3); // The mapping binding pattern used in the `from` clause binds the record fields with the // specified variable names for every value in the `personList`. var personInfo = from var {id: personId, fname: firstName, lname: lastName} in personList select { id: personId, name: firstName + " " + lastName }; io:println(personInfo); } function getPersonInfo() returns [string, int][] { return [["John", 30], ["Anne", 24], ["Mike", 21]]; } function getPersonList() returns Person[] { return [{id: 1001, fname: "Anne", lname: "Frank"}, {id: 1002, fname: "John", lname: "Hardy"}]; } ================================================ FILE: examples/iterative-use-of-typed-binding/iterative_use_of_typed_binding.md ================================================ # Iterative use of typed binding patterns The iterative use is always followed by the `in` keyword. In this approach, an iterator gets created as a result of evaluating an expression, and the typed binding pattern is matched against each value returned by the iterator. The `in` keyword can be used with the `foreach` statement, `from` clause, and `join` clause in query expressions, and with the `let` expression. ::: code iterative_use_of_typed_binding.bal ::: ::: out iterative_use_of_typed_binding.out ::: ## Related links - [Binding patterns](/learn/by-example/binding-patterns/) - [Typed binding pattern](/learn/by-example/typed-binding-pattern/) - [Single use of typed binding patterns](/learn/by-example/single-use-of-typed-binding/) - [Query expressions](/learn/by-example/query-expressions/) - [Foreach statement](/learn/by-example/foreach-statement/) ================================================ FILE: examples/iterative-use-of-typed-binding/iterative_use_of_typed_binding.metatags ================================================ description: This BBE demonstrates the iterative use of typed binding patterns, iterative use of the list binding, iterative use of mapping binding, iterative use of error binding, typed binding followed by `in`, list binding followed by `in`, mapping binding followed by `in`, error binding followed by `in`, binding pattern in the `from` clause, binding pattern in `join` clause, binding pattern in `let`, and binding pattern in `foreach`. keywords: ballerina, ballerina by example, bbe, binding pattern, typed binding, iterative, list binding, mapping binding, error binding, from, join, let, foreach ================================================ FILE: examples/iterative-use-of-typed-binding/iterative_use_of_typed_binding.out ================================================ $ bal run iterative_use_of_typed_binding.bal Average age of John Anne Mike: 25 [{"id":1001,"name":"Anne Frank"},{"id":1002,"name":"John Hardy"}] ================================================ FILE: examples/jdbc-atomic-transaction/jdbc_atomic_transaction.out ================================================ # Create a Ballerina project. # Copy the example to the project and add the relevant database driver JAR details to the `Ballerina.toml` file. # Execute the command below to build and run the project. $ bal run time = 2022-06-22T13:55:32.037+05:30 level = ERROR module = "" message = "Error while executing SQL query: INSERT INTO Customers (firstName, lastName, registrationID, creditLimit, country)\n VALUES (\'Peter\', \'Stuart\', 4, 5000.75, \'USA\'). Unique index or primary key violation: \"PUBLIC.CONSTRAINT_INDEX_6 ON PUBLIC.CUSTOMERS(REGISTRATIONID NULLS FIRST) VALUES ( /* 2 */ 4 )\"; SQL statement:\nINSERT INTO Customers (firstName, lastName, registrationID, creditLimit, country)\n VALUES (\'Peter\', \'Stuart\', 4, 5000.75, \'USA\') [23505-206]." time = 2022-06-22T13:55:32.038+05:30 level = INFO module = "" message = "One of the queries failed. Rollback transaction." ================================================ FILE: examples/jms-consumer-acknowledgement/jms_consumer_acknowledgement.bal ================================================ import ballerina/io; import ballerinax/java.jms; import ballerinax/activemq.driver as _; public function main() returns error? { jms:Connection connection = check new ( initialContextFactory = "org.apache.activemq.jndi.ActiveMQInitialContextFactory", providerUrl = "tcp://localhost:61616" ); jms:Session session = check connection->createSession(jms:CLIENT_ACKNOWLEDGE); jms:MessageConsumer consumer = check session.createConsumer(destination = { 'type: jms:QUEUE, name: "order-queue" }); jms:Message? message = check consumer->receive(5000); if message is jms:MapMessage { map payload = message.content; int|error orderId = payload["orderId"].ensureType(); if orderId is int { io:println("Received message from the JMS provider", orderId); check consumer->acknowledge(message); } } } ================================================ FILE: examples/jms-consumer-acknowledgement/jms_consumer_acknowledgement.md ================================================ # JMS message consumer - Manual acknowledgment The `jms:MessageConsumer` allows manual acknowledgment for received messages. A `jms:MessageConsumer` can be initialized by using a `jms:Session` object. To configure manual acknowledgment mode, create a `jms:Session` object with the acknowledge mode set to `jms:CLIENT_ACKNOWLEDGE`. ::: code jms_consumer_acknowledgement.bal ::: ## Prerequisites Start a [ActiveMQ broker](https://activemq.apache.org/getting-started) instance. Run the program by executing the following command. ::: out jms_consumer_acknowledgement.out ::: >**Tip:** Run the JMS message producer given in the [JMS message producer - Produce message](/learn/by-example/jms-producer-produce-message) example to produce a few sample messages to the queue. ## Related links - [`jms:MessageConsumer->acknowledge` function - API documentation](https://lib.ballerina.io/ballerinax/java.jms/latest#MessageConsumer-acknowledge) - [`jms:MessageConsumer` functions - Specification](https://github.com/ballerina-platform/module-ballerinax-java.jms/blob/master/docs/spec/spec.md#61-functions) ================================================ FILE: examples/jms-consumer-acknowledgement/jms_consumer_acknowledgement.metatags ================================================ description: BBE on consuming messages from a JMS provider using Ballerina. keywords: ballerina, ballerina by example, JMS, message consumer, acknowledgement, bbe ================================================ FILE: examples/jms-consumer-acknowledgement/jms_consumer_acknowledgement.out ================================================ $ bal run jms_consumer_acknowledgement.bal ================================================ FILE: examples/jms-consumer-consume-message/jms_consumer_consume_message.bal ================================================ import ballerina/io; import ballerinax/java.jms; import ballerinax/activemq.driver as _; public function main() returns error? { jms:Connection connection = check new ( initialContextFactory = "org.apache.activemq.jndi.ActiveMQInitialContextFactory", providerUrl = "tcp://localhost:61616" ); jms:Session session = check connection->createSession(); jms:MessageConsumer consumer = check session.createConsumer(destination = { 'type: jms:QUEUE, name: "order-queue" }); jms:Message? message = check consumer->receive(5000); if message is jms:MapMessage { io:println("Received message from the JMS provider", message); } } ================================================ FILE: examples/jms-consumer-consume-message/jms_consumer_consume_message.md ================================================ # JMS message consumer - Consume messages The `jms:MessageConsumer` allows fetching individual messages one by one from a given JMS provider. A `jms:MessageConsumer` can be initialized by using a `jms:Session` object. To pull messages from the JMS provider, use either of the `receive` or `receiveNoWait` methods. It is possible to use automatic or manual acknowledgments similar to the consumer service. Use it to pull messages one by one from a JMS destination in the JMS provider. ::: code jms_consumer_consume_message.bal ::: ## Prerequisites Start a [ActiveMQ broker](https://activemq.apache.org/getting-started) instance. Run the program by executing the following command. ::: out jms_consumer_consume_message.out ::: >**Tip:** Run the JMS message producer given in the [JMS message producer - Produce message](/learn/by-example/jms-producer-produce-message) example to produce some messages to the queue. ## Related links - [`jms:MessageConsumer->receive` function - API documentation](https://lib.ballerina.io/ballerinax/java.jms/latest#MessageConsumer-receive) - [`jms:MessageConsumer->receiveNoWait` function - API documentation](https://lib.ballerina.io/ballerinax/java.jms/latest#MessageConsumer-receiveNoWait) - [`jms:MessageConsumer` functions - Specification](https://github.com/ballerina-platform/module-ballerinax-java.jms/blob/master/docs/spec/spec.md#61-functions) ================================================ FILE: examples/jms-consumer-consume-message/jms_consumer_consume_message.metatags ================================================ description: BBE on consuming messages from a JMS provider using Ballerina. keywords: ballerina, ballerina by example, JMS, message consumer, bbe ================================================ FILE: examples/jms-consumer-consume-message/jms_consumer_consume_message.out ================================================ $ bal run jms_consumer_consume_message.bal ================================================ FILE: examples/jms-producer-produce-message/jms_producer_produce_message.bal ================================================ import ballerina/http; import ballerinax/java.jms; import ballerinax/activemq.driver as _; service / on new http:Listener(9090) { private final jms:MessageProducer orderProducer; function init() returns error? { jms:Connection connection = check new ( initialContextFactory = "org.apache.activemq.jndi.ActiveMQInitialContextFactory", providerUrl = "tcp://localhost:61616" ); jms:Session session = check connection->createSession(); self.orderProducer = check session.createProducer({ 'type: jms:QUEUE, name: "order-queue" }); } resource function post orders(map payoad) returns http:Accepted|error { jms:MapMessage message = { content: payoad }; check self.orderProducer->send(message); return http:ACCEPTED; } } ================================================ FILE: examples/jms-producer-produce-message/jms_producer_produce_message.curl.out ================================================ $ curl http://localhost:9090/orders -H "Content-type:application/json" -d '{"orderId": 1, "productName": "Sport shoe", "price": 27.5, "isValid": true}' ================================================ FILE: examples/jms-producer-produce-message/jms_producer_produce_message.md ================================================ # JMS message producer - Produce messages The `jms:MessageProducer` connects to a given JMS provider, and then sends messages to a specific JMS destination (queue or a topic) in the JMS provider. A `jms:MessageProducer` can be initialized by using a `jms:Session` object. Once connected, the `send` method can be used to send messages to a JMS destination in the JMS provider. `jms:TextMessage`, `jms:BytesMessage`, and `jms:MapMesage` can be provided as messages. Use this to send messages to a specific JMS destination. The `sentTo` method of the `jms:MessageProducer` is used to send messages to multiple JMS destinations. ::: code jms_producer_produce_message.bal ::: ## Prerequisites Start a [ActiveMQ broker](https://activemq.apache.org/getting-started) instance. Run the program by executing the following command. ::: out jms_producer_produce_message.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out jms_producer_produce_message.curl.out ::: ## Related links - [`jms:MessageProducer->send` function - API documentation](https://lib.ballerina.io/ballerinax/java.jms/latest#MessageProducer-send) - [`jms:MessageProducer->sendTo` function - API documentation](https://lib.ballerina.io/ballerinax/java.jms/latest#MessageProducer-sendTo) - [`jms:MessageProducer` functions - Specification](https://github.com/ballerina-platform/module-ballerinax-java.jms/blob/master/docs/spec/spec.md#51-functions) ================================================ FILE: examples/jms-producer-produce-message/jms_producer_produce_message.metatags ================================================ description: BBE on sending messages to a JMS destination using a `jms:MessageProducer` client. keywords: ballerina, ballerina by example, bbe, JMS, message producer ================================================ FILE: examples/jms-producer-produce-message/jms_producer_produce_message.out ================================================ $ bal run jms_producer_produce_message.bal ================================================ FILE: examples/jms-producer-transaction/jms_producer_transaction.bal ================================================ import ballerina/http; import ballerinax/java.jms; import ballerinax/activemq.driver as _; service / on new http:Listener(9090) { private final jms:MessageProducer orderProducer; private final jms:Session session; function init() returns error? { jms:Connection connection = check new ( initialContextFactory = "org.apache.activemq.jndi.ActiveMQInitialContextFactory", providerUrl = "tcp://localhost:61616" ); self.session = check connection->createSession(jms:SESSION_TRANSACTED); self.orderProducer = check self.session.createProducer(); } resource function post orders(map payoad) returns http:Accepted|error { jms:MapMessage message = { content: payoad }; do { check self.orderProducer->sendTo({'type: jms:QUEUE, name: "order-queue"}, message); check self.session->'commit(); return http:ACCEPTED; } on fail error err { check self.session->'rollback(); return err; } } } ================================================ FILE: examples/jms-producer-transaction/jms_producer_transaction.curl.out ================================================ $ curl http://localhost:9090/orders -H "Content-type:application/json" -d '{"orderId": 1, "productName": "Sport shoe", "price": 27.5, "isValid": true}' ================================================ FILE: examples/jms-producer-transaction/jms_producer_transaction.md ================================================ # JMS message producer - Transactions The `jms:MessageProducer` can become a transactional producer via the `'commit'` and `'rollback` functionalities of the `jms:Session`. Upon successful execution of the transaction block, the `jms:Session` can commit or roll back in the case of any error. Use this to send messages atomically to a JMS provider. ::: code jms_producer_transaction.bal ::: ## Prerequisites Start a [ActiveMQ broker](https://activemq.apache.org/getting-started) instance. Run the program by executing the following command. ::: out jms_producer_transaction.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out jms_producer_transaction.curl.out ::: ## Related links - [`jms:Session->'commit` function - API documentation](https://lib.ballerina.io/ballerinax/java.jms/latest#Session-commit) - [`jms:Session->'rollback` function - API documentation](https://lib.ballerina.io/ballerinax/java.jms/latest#Session-rollback) - [`jms:Session` functions - Specification](https://github.com/ballerina-platform/module-ballerinax-java.jms/blob/master/docs/spec/spec.md#32-functions) ================================================ FILE: examples/jms-producer-transaction/jms_producer_transaction.metatags ================================================ description: BBE on sending messages transactionally to a JMS destination using a `jms:MessageProducer` client. keywords: ballerina, ballerina by example, bbe, JMS, message producer, transaction ================================================ FILE: examples/jms-producer-transaction/jms_producer_transaction.out ================================================ $ bal run jms_producer_transaction.bal ================================================ FILE: examples/jms-service-consume-message/jms_service_consume_message.bal ================================================ import ballerinax/java.jms; import ballerina/log; import ballerinax/activemq.driver as _; listener jms:Listener jmsListener = check new ( initialContextFactory = "org.apache.activemq.jndi.ActiveMQInitialContextFactory", providerUrl = "tcp://localhost:61616" ); @jms:ServiceConfig { queueName: "order-queue" } service on jmsListener { remote function onMessage(jms:Message message) returns error? { if message is jms:MapMessage { log:printInfo("Order message received", content = message.content); } } } ================================================ FILE: examples/jms-service-consume-message/jms_service_consume_message.md ================================================ # JMS service - Consume messages The `jms:Service` connects to a given JMS provider via the `jms:Listener`, and allows receiving messages asynchronously. A `jms:Listener` is initialized by providing the connection configurations. A `jms:Service` must be configured to subscribe to a JMS destination—either a queue or a topic—using the `jms:ServiceConfig` annotation. Use this to listen to messages sent to a particular JMS destination asynchronously. ::: code jms_service_consume_message.bal ::: ## Prerequisites Start a [ActiveMQ broker](https://activemq.apache.org/getting-started) instance. Run the program by executing the following command. ::: out jms_service_consume_message.out ::: >**Tip:** Run the JMS message producer given in the [JMS message producer - Produce message](/learn/by-example/jms-producer-produce-message) example to produce some messages to the queue. ## Related links - [`jms:Listener` - API documentation](https://lib.ballerina.io/ballerinax/java.jms/latest#Listener) - [`jms:Listener` - Specification](https://github.com/ballerina-platform/module-ballerinax-java.jms/blob/master/docs/spec/spec.md#7-message-listener) ================================================ FILE: examples/jms-service-consume-message/jms_service_consume_message.metatags ================================================ description: BBE on consuming messages from a JMS provider in an asynchronous manner using Ballerina. keywords: ballerina, ballerina by example, JMS, message consumer, bbe, service, asynchronous ================================================ FILE: examples/jms-service-consume-message/jms_service_consume_message.out ================================================ $ bal run jms_service_consume_message.bal ================================================ FILE: examples/joining-iterable-objects/joining_iterable_objects.bal ================================================ import ballerina/io; type User record {| int id; string name; |}; type Login record {| int userId; string time; |}; public function main() { User[] users = [ {id: 1234, name: "Keith"}, {id: 6789, name: "Anne"} ]; Login[] logins = [ {userId: 6789, time: "20:10:23"}, {userId: 1234, time: "10:30:02"}, {userId: 3987, time: "12:05:00"} ]; // Inner equijoin. string[] joinResult = from var login in logins // The `join` clause iterates any iterable value similarly to the // `from` clause. join var user in users // The `on` condition is used to match the `login` with the // `user` based on the `userId`. // The iteration is skipped when the condition is not satisfied. on login.userId equals user.id select string `${user.name} : ${login.time}`; io:println(joinResult); // Outer equijoin. string[] outerEquijoin = from var login in logins outer join var user in users // If there is no matching pair for all members in the `users`, // then, the `user` will be `()`, // and the join-on-condition will be skipped. on login.userId equals user is () ? () : user.id select string `${user is () ? "New user" : user.name} : ${login.time}`; io:println(outerEquijoin); } ================================================ FILE: examples/joining-iterable-objects/joining_iterable_objects.md ================================================ # Join iterable objects A `join` clause performs an inner or left outer equi-join. The result is similar to using the nested `from` clauses and a `where` clause. Variables declared in the query expression before the join clause are accessible only on the left side of the join condition, while variables declared in the join clause are accessible only on the right side of the join condition. ::: code joining_iterable_objects.bal ::: ::: out joining_iterable_objects.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) - [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/joining-iterable-objects/joining_iterable_objects.metatags ================================================ description: This BBE demonstrates the `join` clause in Ballerina, joining two tables, joining two lists, joining two maps, the query join, inner join, outer join, and left outer join. keywords: ballerina, ballerina by example, bbe, join clause, query, query expression, join clause, inner join, left outer join, table ================================================ FILE: examples/joining-iterable-objects/joining_iterable_objects.out ================================================ $ bal run joining_iterable_objects.bal ["Anne : 20:10:23","Keith : 10:30:02"] ["Anne : 20:10:23","Keith : 10:30:02","New user : 12:05:00"] ================================================ FILE: examples/json-numbers/json_numbers.bal ================================================ import ballerina/io; public function main() returns error? { int a = 1; float b = 2.1; decimal c = 3.24; // The `json` type allows `int|float|decimal`. json[] d = [a, b, c]; // `toJsonString()` will convert `int|float|decimal` into the JSON numeric // syntax. string e = d.toJsonString(); io:println(e); // `fromJsonString()` converts JSON numeric syntax into `int`, if possible // and `decimal` otherwise. json f = check e.fromJsonString(); io:println(f); json[] g = f; io:println(typeof g[0]); io:println(typeof g[1]); io:println(typeof g[2]); // `cloneWithType()` or `ensureType()` will convert from `int` or `decimal` // into the user's chosen numeric type. float h = check g[2].ensureType(); io:println(h); // `-0` is an edge case: represented as `float`. string i = "-0"; io:println(typeof check i.fromJsonString()); } ================================================ FILE: examples/json-numbers/json_numbers.md ================================================ # JSON numbers Ballerina has three numeric types; but JSON has one. The `json` type allows `int|float|decimal`. `toJsonString()` will convert `int|float|decimal` into JSON numeric syntax. `fromJsonString()` converts JSON numeric syntax into `int`, if possible, and otherwise `decimal`. `cloneWithType()` or `ensureType()` will convert from `int` or `decimal` into the user's chosen numeric type. The net result is that you can use JSON to exchange the full range of all three Ballerina numeric types. `-0` is an edge case: it is represented as a `float`. ::: code json_numbers.bal ::: ::: out json_numbers.out ::: ## Related links - [Casting JSON to user-defined type](/learn/by-example/casting-json-to-user-defined-type/) - [Converting to user-defined type](/learn/by-example/converting-to-user-defined-type/) - [JSON type](/learn/by-example/json-type/) ================================================ FILE: examples/json-numbers/json_numbers.metatags ================================================ description: This BBE demonstrates how to represent numeric value in JSON, convert JSON to numeric representation, convert numeric value to JSON. keywords: ballerina, ballerina by example, bbe, json, numbers, toJsonString, fromJsonString, ensureType ================================================ FILE: examples/json-numbers/json_numbers.out ================================================ $ bal run json_numbers.bal [1, 2.1, 3.24] [1,2.1,3.24] typedesc 1 typedesc 2.1 typedesc 3.24 3.24 typedesc -0.0 ================================================ FILE: examples/json-to-record/json_to_record.bal ================================================ import ballerina/data.jsondata; import ballerina/io; type Book record { string name; string author; int year; }; json jsonContent = { "name": "Clean Code", "author": "Robert C. Martin", "year": 2008 }; string jsonStr = string ` { "name": "Clean Code", "author": "Robert C. Martin", "year": 2008 }`; public function main() returns error? { // Convert the JSON value to a record type. Book book1 = check jsondata:parseAsType(jsonContent); io:println(book1); // Convert the JSON string to a record type. Book book2 = check jsondata:parseString(jsonStr); io:println(book2); byte[] jsonByteArr = jsonStr.toBytes(); // Convert the JSON byte array to a record type. Book book3 = check jsondata:parseBytes(jsonByteArr); io:println(book3); stream byteBlockStream = new (new ByteBlockGenerator(jsonStr)); // Convert the JSON byte block stream to a record type. Book book4 = check jsondata:parseStream(byteBlockStream); io:println(book4); } // Defines a class called `ByteBlockGenerator`, which implements the `next()` method. // This will be invoked when the `next()` method of the stream gets invoked. class ByteBlockGenerator { private int index = 0; private final byte[] byteArr; private final int arraySize; public function init(string data) { self.byteArr = data.toBytes(); self.arraySize = self.byteArr.length(); } public isolated function next() returns record {|byte[] value;|}|error? { if self.index >= self.arraySize { return; } int startIndex = self.index; self.index = startIndex + 4 > self.arraySize ? self.arraySize : startIndex + 3; return {value: self.byteArr.slice(startIndex, self.index)}; } } ================================================ FILE: examples/json-to-record/json_to_record.md ================================================ # JSON to Record conversion The `data.jsondata` library provides multiple APIs to perform the conversion from JSON data in the form of a `string`, `byte[]`, `byte-block-stream`, and `json` to a Ballerina record. For more information on the underlying module, see the [`data.jsondata` module](https://lib.ballerina.io/ballerina/data.jsondata/latest/). ::: code json_to_record.bal ::: ::: out json_to_record.out ::: ================================================ FILE: examples/json-to-record/json_to_record.metatags ================================================ description: This BBE demonstrates the conversion from JSON source, which can be provided as a string, byte array, byte block stream, or json, into a record in Ballerina. keywords: ballerina, ballerina by example, BBE, json, record, stream, byte array ================================================ FILE: examples/json-to-record/json_to_record.out ================================================ $ bal run json_to_record.bal {"name":"Clean Code","author":"Robert C. Martin","year":2008} {"name":"Clean Code","author":"Robert C. Martin","year":2008} {"name":"Clean Code","author":"Robert C. Martin","year":2008} {"name":"Clean Code","author":"Robert C. Martin","year":2008} ================================================ FILE: examples/json-to-record-with-projection/json_to_record_with_projection.bal ================================================ import ballerina/data.jsondata; import ballerina/io; // Define a closed record type to capture the required fields from the JSON content. type Book record {| string name; string author; |}; json jsonContent = { "name": "Clean Code", "author": "Robert C. Martin", "year": 2008, "publisher": "Prentice Hall" }; string jsonStr = string ` { "name": "The Pragmatic Programmer", "author": "Andrew Hunt, David Thomas", "year": 1999, "publisher": "Addison-Wesley" }`; public function main() returns error? { // Based on the expected type, it selectively converts the JSON content to the record type. Book book = check jsondata:parseAsType(jsonContent); io:println(book); // Based on the expected type, it selectively converts the JSON string to the record type. Book book2 = check jsondata:parseString(jsonStr); io:println(book2); } ================================================ FILE: examples/json-to-record-with-projection/json_to_record_with_projection.md ================================================ # JSON to record conversion with projection The `data.jsondata` library provides multiple APIs to selectively convert required fields from JSON data in the form of a `string`, `byte[]`, `byte-block-stream`, and `json` to a Ballerina record. For more information on the underlying module, see the [`data.jsondata` module](https://lib.ballerina.io/ballerina/data.jsondata/latest/). ::: code json_to_record_with_projection.bal ::: ::: out json_to_record_with_projection.out ::: ================================================ FILE: examples/json-to-record-with-projection/json_to_record_with_projection.metatags ================================================ description: This BBE illustrates how to selectively convert fields from a JSON source, which can be provided as a string, byte array, byte block stream, or json, into a record in Ballerina. keywords: ballerina, ballerina by example, BBE, json, record, data projection ================================================ FILE: examples/json-to-record-with-projection/json_to_record_with_projection.out ================================================ $ bal run json_to_record_with_projection.bal {"name":"Clean Code","author":"Robert C. Martin"} {"name":"The Pragmatic Programmer","author":"Andrew Hunt, David Thomas"} ================================================ FILE: examples/json-type/json_type.bal ================================================ import ballerina/io; type Student record {| int id; string name; |}; public function main() returns error? { // As JSON is a union: `()|boolean|int|float|decimal|string|json[]|map`, // the following cases are allowed. json n = null; json i = 21; json s = "str"; json a = [1, 2]; json m = {"x": n, "y": s, "z": a}; io:println(m); json[] arr = [m, {"x": i}]; io:println(arr); string rawData = "{\"id\": 2, \"name\": \"Georgy\"}"; // Get the `json` value from the string. json j = check rawData.fromJsonString(); io:println(j); // Access the fields of `j` using field access. string name = check j.name; io:println(name); // Convert the `json` into a user-defined type. Student student = check j.cloneWithType(); io:println(student.id); // Convert the user-defined type to a `json`. j = student; io:println(j); } ================================================ FILE: examples/json-type/json_type.md ================================================ # `json` type The `json` type can be explained as a union of the simple basic types, `string`, array of `json`, and `json` mapping. Technically, the `json` type is a union: `()|boolean|int|float|decimal|string|json[]|map`. A `json` value can be converted to and from Ballerina straightforwardly except for the choice of the Ballerina numeric type. Ballerina syntax is compatible with JSON and allows null literal to be compatible with JSON. `json` is `anydata` without `table` and `xml`. `toJson()` recursively converts `anydata` to `json`. Table values are converted to `json` arrays and `xml` values are converted to strings. ::: code json_type.bal ::: ::: out json_type.out ::: ## Related links - [Access JSON elements](/learn/by-example/access-json-elements/) - [Convert from JSON to user-defined type](/learn/by-example/convert-from-json-to-user-defined-type/) - [Convert from user-defined type to JSON](/learn/by-example/converting-from-user-defined-type-to-json/) - [fromJsonString](https://lib.ballerina.io/ballerina/lang.value/0.0.0#fromJsonString) - [cloneWithType](https://lib.ballerina.io/ballerina/lang.value/0.0.0#cloneWithType) ================================================ FILE: examples/json-type/json_type.metatags ================================================ description: This BBE demonstrates how to define JSON, create JSON from basic value, access JSON elements, convert JSON to user-defined type, convert user-defined type to a JSON, and convert string to a JSON in Ballerina. keywords: ballerina, ballerina by example, bbe, json type, json, json array, fromJsonString, check, cloneWithType ================================================ FILE: examples/json-type/json_type.out ================================================ $ bal run json_type.bal {"x":null,"y":"str","z":[1,2]} [{"x":null,"y":"str","z":[1,2]},{"x":21}] {"id":2,"name":"Georgy"} Georgy 2 {"id":2,"name":"Georgy"} ================================================ FILE: examples/jsonpath-expressions/jsonpath_expressions.bal ================================================ import ballerina/data.jsondata; import ballerina/io; public function main() returns error? { json books = [ { title: "The Great Gatsby", author: "F. Scott Fitzgerald", price: 100, year: 1925 }, { title: "To Kill a Mockingbird", author: "Harper Lee", price: 72.5, year: 1960 }, { title: "1984", author: "George Orwell", price: 90, year: 1949 } ]; // JSONPath expression to get the list of titles in the books array. json titles = check jsondata:read(books, `$..title`); io:println(titles); // JSONPath expression to get the list of published years for the // books that have a price value of more than 80. json years = check jsondata:read(books, `$..[?(@.price > 80)].year`); io:println(years); // JSONPath expression to get the total sum of the prices of the books. json sum = check jsondata:read(books, `$..price.sum()`); io:println(sum); } ================================================ FILE: examples/jsonpath-expressions/jsonpath_expressions.md ================================================ # JSONPath expressions Ballerina supports JSONPath expressions against JSON data through [`ballerina/data.jsondata` module](https://lib.ballerina.io/ballerina/data.jsondata/latest/) module to provide a straightforward method for navigating the structure of JSON data. Alternatively, Ballerina supports type based access to JSON data by language itself. ::: code jsonpath_expressions.bal ::: ::: out jsonpath_expressions.out ::: ## Related links - [JSON type](/learn/by-example/json-type/) - [Access JSON elements](/learn/by-example/access-json-elements/) ================================================ FILE: examples/jsonpath-expressions/jsonpath_expressions.metatags ================================================ description: This BBE demonstrates JSON path expressions with Ballerina keywords: ballerina, ballerina by example, bbe, json, jsonpath, jsonpath expressions ================================================ FILE: examples/jsonpath-expressions/jsonpath_expressions.out ================================================ $ bal run jsonpath_expressions.bal ["The Great Gatsby","To Kill a Mockingbird","1984"] [1925,1949] 262.5 ================================================ FILE: examples/kafka-consumer-constraint-validation/kafka_consumer_constraint_validation.bal ================================================ import ballerina/constraint; import ballerinax/kafka; import ballerina/io; type Order record { int orderId; // Add a constraint to only allow string values of length between 30 and 1. @constraint:String {maxLength: 30, minLength: 1} string productName; decimal price; boolean isValid; }; public function main() returns error? { kafka:Consumer orderConsumer = check new (kafka:DEFAULT_URL, { groupId: "order-group-id", topics: "order-topic" }); while true { Order[] orders = check orderConsumer->pollPayload(15); from Order 'order in orders where 'order.isValid do { io:println(string `Received valid order for ${'order.productName}`); }; } } ================================================ FILE: examples/kafka-consumer-constraint-validation/kafka_consumer_constraint_validation.md ================================================ # Kafka consumer - Constraint validation The `kafka:Consumer` connects to a given Kafka server, and then validates the received payloads by the defined constraints. The constraints are added as annotations to the payload record and when the payload is received from the broker, it is validated internally and if validation fails, an error will be logged to the console and the `kafka:Consumer` will be automatically seeked to the next record. This behaviour can be changed by setting `autoSeekOnValidationFailure` configuration to `false`. Then the related error is returned to be handled as needed. The `validation` flag of the`kafka:ConsumerConfiguration` can be set to `false` to stop validating the payloads. Use this to validate the messages received from a Kafka server implicitly. ::: code kafka_consumer_constraint_validation.bal ::: ## Prerequisites - Start a [Kafka broker](https://kafka.apache.org/quickstart) instance. Run the program by executing the following command. ::: out kafka_consumer_constraint_validation.out ::: >**Tip:** Run the Kafka client given in the [Kafka producer - Produce message](/learn/by-example/kafka-producer-produce-message) example with a valid product name (0 < length <= 30), then with an invalid product name and again with a valid product name. ## Related links - [`kafka:PayloadValidationError` error type - API documentation](https://lib.ballerina.io/ballerinax/kafka/3.4.0#PayloadValidationError) - [`kafka:Consumer->seek` function - API documentation](https://lib.ballerina.io/ballerinax/kafka/3.4.0#Consumer#seek) - [`kafka` module - Specification](https://github.com/ballerina-platform/module-ballerinax-kafka/blob/master/docs/spec/spec.md) - [`constraint` module - API documentation](https://lib.ballerina.io/ballerina/constraint/latest) ================================================ FILE: examples/kafka-consumer-constraint-validation/kafka_consumer_constraint_validation.metatags ================================================ description: This example demonstrates validating a payload according to the constraints defined in the payload record. keywords: ballerina, ballerina by example, bbe, kafka, consumer, listener, service, constraint, validation ================================================ FILE: examples/kafka-consumer-constraint-validation/kafka_consumer_constraint_validation.out ================================================ $ bal run kafka_client_constraint_validation.bal time = 2022-11-28T13:56:56.502+05:30 level = INFO module = "" message = "Received valid order for Sport" time = 2022-11-28T14:19:43.346+05:30 level = ERROR module = "" message = "Payload validation failed" error = "Failed to validate payload. If needed, please seek past the record to continue consumption." time = 2022-11-28T13:56:56.502+05:30 level = INFO module = "" message = "Received valid order for Sport" ================================================ FILE: examples/kafka-consumer-consumer-record-data-binding/kafka_consumer_consumer_record_data_binding.bal ================================================ import ballerinax/kafka; import ballerina/io; type Order readonly & record { int orderId; string productName; decimal price; boolean isValid; }; // Create a subtype of `kafka:AnydataConsumerRecord`. type OrderConsumerRecord record {| *kafka:AnydataConsumerRecord; Order value; |}; public function main() returns error? { kafka:Consumer orderConsumer = check new (kafka:DEFAULT_URL, { groupId: "order-group-id", topics: "order-topic" }); while true { // Polls the consumer for order records. OrderConsumerRecord[] records = check orderConsumer->poll(15); from OrderConsumerRecord orderRecord in records where orderRecord.value.isValid do { io:println(string `Received valid order for ${orderRecord.value.productName}`); }; } } ================================================ FILE: examples/kafka-consumer-consumer-record-data-binding/kafka_consumer_consumer_record_data_binding.md ================================================ # Kafka consumer - Consumer record data binding The consumer record data-binding allows you to directly bind Kafka messages to subtypes of `kafka:AnydataConsumerRecord`. It does this by using the built-in bytes deserializer for both the key and the value. To use this, directly assign the `poll` method’s return value to the declared variable, which is a subtype of `kafka:AnydataConsumerRecord[]`. A subtype of `kafka:AnydataConsumerRecord` can be created by specifying a user defined type for the value field. If the record does not match with the defined type, the related error will be logged to the console and the `kafka:Consumer` will be automatically seeked to the next record. This behaviour can be changed by setting `autoSeekOnValidationFailure` configuration to `false`. Then the related error is returned to be handled as needed. Use this to receive messages from a Kafka server with the metadata of the messages like `kafka:PartitionOffset` and `timestamp`. It is important to note that this only works when `kafka:Producer` also uses the built-in bytes serializer for Ballerina. ::: code kafka_consumer_consumer_record_data_binding.bal ::: ## Prerequisites - Start a [Kafka broker](https://kafka.apache.org/quickstart) instance. Run the program by executing the following command. ::: out kafka_consumer_consumer_record_data_binding.out ::: >**Tip:** Run the Kafka client given in the [Kafka producer - Produce message](/learn/by-example/kafka-producer-produce-message) example to produce some messages to the topic. ## Related links - [`kafka:Consumer->poll` function - API documentation](https://lib.ballerina.io/ballerinax/kafka/latest#Consumer#poll) - [Kafka client consume messages - Specification](https://github.com/ballerina-platform/module-ballerinax-kafka/blob/master/docs/spec/spec.md#422-consume-messages) ================================================ FILE: examples/kafka-consumer-consumer-record-data-binding/kafka_consumer_consumer_record_data_binding.metatags ================================================ description: This example demonstrates how to use a kafka:Consumer as a simple record consumer. keywords: ballerina, ballerina by example, bbe, kafka, consumer, client ================================================ FILE: examples/kafka-consumer-consumer-record-data-binding/kafka_consumer_consumer_record_data_binding.out ================================================ $ bal run kafka_client_consumer_poll_consumer_record.bal Received valid order for Sport shoe ================================================ FILE: examples/kafka-consumer-payload-data-binding/kafka_consumer_payload_data_binding.bal ================================================ import ballerinax/kafka; import ballerina/io; type Order readonly & record { int orderId; string productName; decimal price; boolean isValid; }; public function main() returns error? { kafka:Consumer orderConsumer = check new (kafka:DEFAULT_URL, { groupId: "order-group-id", topics: "order-topic" }); while true { // Polls the consumer for payload. Order[] orders = check orderConsumer->pollPayload(15); from Order 'order in orders where 'order.isValid do { io:println(string `Received valid order for ${'order.productName}`); }; } } ================================================ FILE: examples/kafka-consumer-payload-data-binding/kafka_consumer_payload_data_binding.md ================================================ # Kafka consumer - Payload data binding The payload data-binding allows you to directly bind Kafka messages to subtypes of `anydata`. It does this by using the built-in bytes deserializer for both the key and the value. To use this, directly assign the `pollPayload` method’s return value to the declared variable, which is a subtype of `anydata[]`. If the payload does not match with the defined type, the related error will be logged to the console and the `kafka:Consumer` will be automatically seeked to the next record. This behaviour can be changed by setting `autoSeekOnValidationFailure` configuration to `false`. Then the related error is returned to be handled as needed. Use this to receive messages from a Kafka server without the metadata of the messages like `kafka:PartitionOffset` and `timestamp`. It is important to note that this only works when `kafka:Producer` also uses the built-in bytes serializer for Ballerina. ::: code kafka_consumer_payload_data_binding.bal ::: ## Prerequisites - Start a [Kafka broker](https://kafka.apache.org/quickstart) instance. Run the program by executing the following command. ::: out kafka_consumer_payload_data_binding.out ::: >**Tip:** Run the Kafka client given in the [Kafka producer - Produce message](/learn/by-example/kafka-producer-produce-message) example to produce some messages to the topic. ## Related links - [`kafka:Consumer->pollPayload` function - API documentation](https://lib.ballerina.io/ballerinax/kafka/latest#Consumer#pollPayload) - [Kafka client consume messages - Specification](https://github.com/ballerina-platform/module-ballerinax-kafka/blob/master/docs/spec/spec.md#422-consume-messages) ================================================ FILE: examples/kafka-consumer-payload-data-binding/kafka_consumer_payload_data_binding.metatags ================================================ description: This example demonstrates how to use a kafka:Consumer as a payload consumer. keywords: ballerina, ballerina by example, bbe, kafka, consumer, client ================================================ FILE: examples/kafka-consumer-payload-data-binding/kafka_consumer_payload_data_binding.out ================================================ $ bal run kafka_client_consumer_poll_payload.bal Received valid order for Sport shoe ================================================ FILE: examples/kafka-consumer-sasl/kafka_consumer_sasl.bal ================================================ import ballerinax/kafka; import ballerina/io; type Order readonly & record { int orderId; string productName; decimal price; boolean isValid; }; public function main() returns error? { kafka:Consumer orderConsumer = check new ("localhost:9093", { groupId: "order-group-id", topics: "order-topic", // Provide the relevant authentication configurations to authenticate the consumer // by the `kafka:AuthenticationConfiguration`. auth: { // Provide the authentication mechanism used by the Kafka server. mechanism: kafka:AUTH_SASL_PLAIN, // Username and password should be set here in order to authenticate the consumer. username: "alice", password: "alice@123" }, securityProtocol: kafka:PROTOCOL_SASL_PLAINTEXT }); // Polls the consumer for payload. Order[] orders = check orderConsumer->pollPayload(1); from Order 'order in orders where 'order.isValid do { io:println(string `Received valid order for ${'order.productName}`); }; } ================================================ FILE: examples/kafka-consumer-sasl/kafka_consumer_sasl.md ================================================ # Kafka consumer - SASL authentication The `kafka:Consumer` connects to a Kafka server via SASL/PLAIN authentication and then, receives the payloads from the server. SASL/PLAIN authentication can be enabled by configuring the `auth`, which requires the authentication mechanism, username, and password. Further, the mode of security must be configured by setting the `securityProtocol` to `kafka:PROTOCOL_SASL_PLAINTEXT`. Use this to connect to a Kafka server secured with SASL/PLAIN. ::: code kafka_consumer_sasl.bal ::: ## Prerequisites - Start a [Kafka broker](https://kafka.apache.org/quickstart) instance configured to use the [SASL/PLAIN authentication mechanism](https://docs.confluent.io/platform/current/kafka/authentication_sasl/authentication_sasl_plain.html#sasl-plain-overview). Run the program by executing the following command. ::: out kafka_consumer_sasl.out ::: >**Tip:** Run the Kafka client given in the [Kafka producer - SASL authentication](/learn/by-example/kafka-producer-sasl) example to produce some messages to the topic. ## Related links - [`kafka:AuthenticationConfiguration` record - API documentation](https://lib.ballerina.io/ballerinax/kafka/latest#AuthenticationConfiguration) - [Kafka client consumer SASL authentication - Specification](https://github.com/ballerina-platform/module-ballerinax-kafka/blob/master/docs/spec/spec.md#4212-secure-client) ================================================ FILE: examples/kafka-consumer-sasl/kafka_consumer_sasl.metatags ================================================ description: This example is on how to configure a Kafka consumer to use SASL/PLAIN authentication. keywords: ballerina, ballerina by example, bbe, kafka, consumer, authentication, SASL ================================================ FILE: examples/kafka-consumer-sasl/kafka_consumer_sasl.out ================================================ $ bal run kafka_client_consumer_sasl.bal Received valid order for Sport shoe ================================================ FILE: examples/kafka-consumer-ssl/kafka_consumer_ssl.bal ================================================ import ballerinax/kafka; import ballerina/io; type Order readonly & record { int orderId; string productName; decimal price; boolean isValid; }; public function main() returns error? { kafka:Consumer orderConsumer = check new ("localhost:9094", { groupId: "order-group-id", topics: ["order-topic"], // Provide the relevant secure socket configurations by using `kafka:SecureSocket`. secureSocket: { cert: "./resources/path/to/public.crt", protocol: { // Provide the relevant security protocol. name: kafka:SSL } }, // Provide the type of the security protocol to use in the broker connection. securityProtocol: kafka:PROTOCOL_SSL }); // Polls the consumer for payload. Order[] orders = check orderConsumer->pollPayload(1); from Order 'order in orders where 'order.isValid do { io:println(string `Received valid order for ${'order.productName}`); }; } ================================================ FILE: examples/kafka-consumer-ssl/kafka_consumer_ssl.md ================================================ # Kafka consumer - SSL/TLS The `kafka:Consumer` connects to a Kafka server via SSL/TLS and then, receives payloads from the server. SSL/TLS can be enabled by configuring the `secureSocket`, which requires a certificate and the protocol name. Further, the mode of security must be configured by setting the `securityProtocol` to `kafka:PROTOCOL_SSL`. Use this to connect to a Kafka server secured with SSL/TLS. ::: code kafka_consumer_ssl.bal ::: ## Prerequisites - Start a [Kafka broker](https://kafka.apache.org/quickstart) instance configured to use [SSL/TLS](https://docs.confluent.io/3.0.0/kafka/ssl.html#configuring-kafka-brokers). Run the program by executing the following command. ::: out kafka_consumer_ssl.out ::: >**Tip:** Run the Kafka client given in the [Kafka producer - SSL/TLS](/learn/by-example/kafka-producer-ssl) example to produce some messages to the topic. ## Related links - [`kafka:SecureSocket` record - API documentation](https://lib.ballerina.io/ballerinax/kafka/latest#SecureSocket) - [Kafka secure client - Specification](https://github.com/ballerina-platform/module-ballerinax-kafka/blob/master/docs/spec/spec.md#4212-secure-client) ================================================ FILE: examples/kafka-consumer-ssl/kafka_consumer_ssl.metatags ================================================ description: This example is on how to configure a Kafka consumer to use SSL encryption. keywords: ballerina, ballerina by example, bbe, kafka, consumer, authentication, SASL ================================================ FILE: examples/kafka-consumer-ssl/kafka_consumer_ssl.out ================================================ $ bal run kafka_client_consumer_sasl.bal Received valid order for Sport shoe ================================================ FILE: examples/kafka-producer-produce-message/kafka_producer_produce_message.bal ================================================ import ballerinax/kafka; import ballerina/http; type Order readonly & record { int orderId; string productName; decimal price; boolean isValid; }; service / on new http:Listener(9090) { private final kafka:Producer orderProducer; function init() returns error? { self.orderProducer = check new (kafka:DEFAULT_URL); } resource function post orders(Order newOrder) returns http:Accepted|error { check self.orderProducer->send({ topic: "order-topic", value: newOrder }); return http:ACCEPTED; } } ================================================ FILE: examples/kafka-producer-produce-message/kafka_producer_produce_message.curl.out ================================================ $ curl http://localhost:9090/orders -H "Content-type:application/json" -d "{\"orderId\": 1, \"productName\": \"Sport shoe\", \"price\": 27.5, \"isValid\": true}" ================================================ FILE: examples/kafka-producer-produce-message/kafka_producer_produce_message.md ================================================ # Kafka producer - Produce message The `kafka:Producer` connects to a given Kafka server, and then sends messages to a specific topic in the server. A `kafka:Producer` is created by giving the Kafka server url. Once connected, `send` method is used to send messages to the Kafka server by providing the relevant topic and the value as the parameters. `anydata` and subtypes of `anydata` can be provided as the values, and these will be serialized using the built-in byte array serializer internally when sending to the server. Use this to send messages to a topic in the Kafka server. ::: code kafka_producer_produce_message.bal ::: ## Prerequisites - Start a [Kafka broker](https://kafka.apache.org/quickstart) instance. Run the program by executing the following command. ::: out kafka_producer_produce_message.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out kafka_producer_produce_message.curl.out ::: ## Related links - [`kafka:Producer->send` function - API documentation](https://lib.ballerina.io/ballerinax/kafka/latest#Producer#send) - [`kafka:Producer` functions - Specification](https://github.com/ballerina-platform/module-ballerinax-kafka/blob/master/docs/spec/spec.md#33-functions) ================================================ FILE: examples/kafka-producer-produce-message/kafka_producer_produce_message.metatags ================================================ description: This example demonstrates sending messages to a Kafka topic using a kafka:Producer client. keywords: ballerina, ballerina by example, bbe, kafka, producer ================================================ FILE: examples/kafka-producer-produce-message/kafka_producer_produce_message.out ================================================ $ bal run kafka_client_producer.bal ================================================ FILE: examples/kafka-producer-sasl/kafka_producer_sasl.bal ================================================ import ballerina/http; import ballerinax/kafka; type Order readonly & record { int orderId; string productName; decimal price; boolean isValid; }; service / on new http:Listener(9090) { private final kafka:Producer orderProducer; function init() returns error? { self.orderProducer = check new ("localhost:9093", { // Provide the relevant authentication configurations to authenticate the producer by // `kafka:AuthenticationConfiguration`. auth: { // Provide the authentication mechanism used by the Kafka server. mechanism: kafka:AUTH_SASL_PLAIN, // Username and password should be set here in order to authenticate the producer. username: "alice", password: "alice@123" }, securityProtocol: kafka:PROTOCOL_SASL_PLAINTEXT }); } resource function post orders(Order newOrder) returns http:Accepted|error { check self.orderProducer->send({ topic: "order-topic", value: newOrder }); return http:ACCEPTED; } } ================================================ FILE: examples/kafka-producer-sasl/kafka_producer_sasl.curl.out ================================================ $ curl http://localhost:9090/orders -H "Content-type:application/json" -d "{\"orderId\": 1, \"productName\": \"Sport shoe\", \"price\": 27.5, \"isValid\": true}" ================================================ FILE: examples/kafka-producer-sasl/kafka_producer_sasl.md ================================================ # Kafka producer - SASL authentication The `kafka:Producer` connects to a Kafka server via SASL/PLAIN authentication and then, sends messages to the server. SASL/PLAIN authentication can be enabled by configuring the `auth`, which requires the authentication mechanism, username, and password. Further, the mode of security must be configured by setting the `securityProtocol` to `kafka:PROTOCOL_SASL_PLAINTEXT`. Use this to connect to a Kafka server secured with SASL/PLAIN. ::: code kafka_producer_sasl.bal ::: ## Prerequisites - Start a [Kafka broker](https://kafka.apache.org/quickstart) instance configured to use the [SASL/PLAIN authentication mechanism](https://docs.confluent.io/platform/current/kafka/authentication_sasl/authentication_sasl_plain.html#sasl-plain-overview). Run the program by executing the following command. ::: out kafka_producer_sasl.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out kafka_producer_sasl.curl.out ::: ## Related links - [`kafka:AuthenticationConfiguration` record - API documentation](https://lib.ballerina.io/ballerinax/kafka/latest#AuthenticationConfiguration) - [Kafka client producer SASL authentication - Specification](https://github.com/ballerina-platform/module-ballerinax-kafka/blob/master/docs/spec/spec.md#322-secure-client) ================================================ FILE: examples/kafka-producer-sasl/kafka_producer_sasl.metatags ================================================ description: This example is on how to configure a Kafka producer to use SASL/PLAIN authentication. keywords: ballerina, ballerina by example, bbe, kafka, producer, authentication, SASL ================================================ FILE: examples/kafka-producer-sasl/kafka_producer_sasl.out ================================================ $ bal run kafka_client_producer_sasl.bal ================================================ FILE: examples/kafka-producer-ssl/kafka_producer_ssl.bal ================================================ import ballerina/http; import ballerinax/kafka; type Order readonly & record { int orderId; string productName; decimal price; boolean isValid; }; service / on new http:Listener(9090) { private final kafka:Producer orderProducer; function init() returns error? { self.orderProducer = check new ("localhost:9094", { // Provide the relevant secure socket configurations by using `kafka:SecureSocket`. secureSocket: { cert: "./resources/path/to/public.crt", protocol: { // Provide the relevant security protocol. name: kafka:SSL } }, // Provide the type of the security protocol to use in the broker connection. securityProtocol: kafka:PROTOCOL_SSL }); } resource function post orders(Order newOrder) returns http:Accepted|error { check self.orderProducer->send({ topic: "order-topic", value: newOrder }); return http:ACCEPTED; } } ================================================ FILE: examples/kafka-producer-ssl/kafka_producer_ssl.curl.out ================================================ $ curl http://localhost:9090/orders -H "Content-type:application/json" -d "{\"orderId\": 1, \"productName\": \"Sport shoe\", \"price\": 27.5, \"isValid\": true}" ================================================ FILE: examples/kafka-producer-ssl/kafka_producer_ssl.md ================================================ # Kafka producer - SSL/TLS The `kafka:Producer` connects to a Kafka server via SSL/TLS and then, sends messages to the server. SSL/TLS can be enabled by configuring the `secureSocket`, which requires a certificate and the protocol name. Further, the mode of security must be configured by setting the `securityProtocol` to `kafka:PROTOCOL_SSL`. Use this to connect to a Kafka server secured with SSL. >**Info:** For more information on the underlying module, see the [`kafka` module](https://lib.ballerina.io/ballerinax/kafka/latest). ::: code kafka_producer_ssl.bal ::: ## Prerequisites - Start a [Kafka broker](https://kafka.apache.org/quickstart) instance configured to use [SSL/TLS](https://docs.confluent.io/3.0.0/kafka/ssl.html#configuring-kafka-brokers). Run the program by executing the following command. ::: out kafka_producer_ssl.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out kafka_producer_ssl.curl.out ::: ## Related links - [`kafka:SecureSocket` record - API documentation](https://lib.ballerina.io/ballerinax/kafka/latest#SecureSocket) - [Kafka secure client - Specification](https://github.com/ballerina-platform/module-ballerinax-kafka/blob/master/docs/spec/spec.md#322-secure-client) ================================================ FILE: examples/kafka-producer-ssl/kafka_producer_ssl.metatags ================================================ description: This example is on how to configure a Kafka producer to use SSL encryption. keywords: ballerina, ballerina by example, bbe, kafka, producer, encryption, SSL ================================================ FILE: examples/kafka-producer-ssl/kafka_producer_ssl.out ================================================ $ bal run kafka_client_producer_ssl.bal Message published successfully. ================================================ FILE: examples/kafka-service-constraint-validation/kafka_service_constraint_validation.bal ================================================ import ballerina/constraint; import ballerinax/kafka; import ballerina/log; type Order record { int orderId; // Add a constraint to only allow string values of length between 30 and 1. @constraint:String {maxLength: 30, minLength: 1} string productName; decimal price; boolean isValid; }; listener kafka:Listener orderListener = new (kafka:DEFAULT_URL, { groupId: "order-group-id", topics: "order-topic" }); service on orderListener { remote function onConsumerRecord(Order[] orders) { from Order 'order in orders where 'order.isValid do { log:printInfo(string `Received valid order for ${'order.productName}`); }; } } ================================================ FILE: examples/kafka-service-constraint-validation/kafka_service_constraint_validation.md ================================================ # Kafka service - Constraint validation The `kafka:Service` connects to a given Kafka server via the `kafka:Listener`, and then validates the received payloads by the defined constraints. The constraints are added as annotations to the payload record and when the payload is received from the broker, it is validated internally and if validation fails, a `kafka:PayloadBindingError` will be logged to the console and the `kafka:Listener` will be automatically seeked to the next record. This behaviour can be changed by setting `autoSeekOnValidationFailure` configuration to `false`. Then the `onError` remote method will be invoked with the related error to be handled as needed. The `validation` flag of the`kafka:ConsumerConfiguration` can be set to `false` to stop validating the payloads. Use this to validate the messages received from a Kafka server implicitly. ::: code kafka_service_constraint_validation.bal ::: ## Prerequisites - Start a [Kafka broker](https://kafka.apache.org/quickstart) instance. Run the program by executing the following command. ::: out kafka_service_constraint_validation.out ::: >**Tip:** Run the Kafka client given in the [Kafka producer - Produce message](/learn/by-example/kafka-producer-produce-message) example with a valid product name (0 < length <= 30), then with an invalid product name and again with a valid product name. ## Related links - [`kafka:PayloadValidationError` error type - API documentation](https://lib.ballerina.io/ballerinax/kafka/latest#PayloadValidationError) - [`kafka:Caller->seek` function - API documentation](https://lib.ballerina.io/ballerinax/kafka/latest#Caller#seek) - [`kafka` module - Specification](https://github.com/ballerina-platform/module-ballerinax-kafka/blob/master/docs/spec/spec.md) - [`constraint` module - API documentation](https://lib.ballerina.io/ballerina/constraint/latest) ================================================ FILE: examples/kafka-service-constraint-validation/kafka_service_constraint_validation.metatags ================================================ description: This example demonstrates validating a payload according to the constraints defined in the payload record. keywords: ballerina, ballerina by example, bbe, kafka, consumer, listener, service, constraint, validation ================================================ FILE: examples/kafka-service-constraint-validation/kafka_service_constraint_validation.out ================================================ $ bal run kafka_service_constraint_validation.bal time = 2022-11-28T13:56:56.502+05:30 level = INFO module = "" message = "Received valid order for Sport shoe" error: Failed to validate payload. {"partition":{"topic":"order-topic-4","partition":0},"offset":10} cause: Validation failed for '$.productName:maxLength' constraint(s). time = 2022-11-28T13:56:56.502+05:30 level = INFO module = "" message = "Received valid order for Sport shoe" ================================================ FILE: examples/kafka-service-consume-message/kafka_service_consume_message.bal ================================================ import ballerinax/kafka; import ballerina/log; type Order readonly & record { int orderId; string productName; decimal price; boolean isValid; }; listener kafka:Listener orderListener = new (kafka:DEFAULT_URL, { groupId: "order-group-id", topics: "order-topic" }); service on orderListener { remote function onConsumerRecord(Order[] orders) { // The set of orders received by the service are processed one by one. from Order 'order in orders where 'order.isValid do { log:printInfo(string `Received valid order for ${'order.productName}`); }; } } ================================================ FILE: examples/kafka-service-consume-message/kafka_service_consume_message.md ================================================ # Kafka service - Consume message The `kafka:Service` connects to a given Kafka server via the `kafka:Listener`, and allows to directly bind Kafka messages to subtypes of `anydata`. It does this by using the built-in bytes deserializer for both the key and the value. To use this, provide the required payload type as the argument type to the `onConsumerRecord` method, which is a subtype of `anydata[]`. When new messages are received, the `onConsumerRecord` method gets invoked. If the payload does not match the defined type, a `kafka:PayloadBindingError` will be logged to the console and the `kafka:Listener` will be automatically seeked to the next record. This behaviour can be changed by setting `autoSeekOnValidationFailure` configuration to `false`. Then the `onError` remote method will be invoked with the related error to be handled as needed. Use this to listen to a set of topics in a Kafka server and receive messages implicitly. ::: code kafka_service_consume_message.bal ::: ## Prerequisites - Start a [Kafka broker](https://kafka.apache.org/quickstart) instance. Run the program by executing the following command. ::: out kafka_service_consume_message.out ::: >**Tip:** Run the Kafka client given in the [Kafka producer - Produce message](/learn/by-example/kafka-producer-produce-message) example to produce some messages to the topic. ## Related links - [`kafka:Listener` client object - API documentation](https://lib.ballerina.io/ballerinax/kafka/latest#Listener) - [Kafka service - Specification](https://github.com/ballerina-platform/module-ballerinax-kafka/blob/master/docs/spec/spec.md#432-usage) ================================================ FILE: examples/kafka-service-consume-message/kafka_service_consume_message.metatags ================================================ description: This example demonstrates creating a Kafka service to receive messages in an asynchronous manner. keywords: ballerina, ballerina by example, bbe, kafka, consumer, listener, service, asynchronous ================================================ FILE: examples/kafka-service-consume-message/kafka_service_consume_message.out ================================================ $ bal run kafka_service.bal time = 2022-11-25T14:55:59.366+05:30 level = INFO module = "" message = "Received valid order for Sport shoe" ================================================ FILE: examples/kafka-service-error-handling/kafka_service_error_handling.bal ================================================ import ballerinax/kafka; import ballerina/log; type Order readonly & record { int orderId; string productName; decimal price; boolean isValid; int quantity; }; listener kafka:Listener orderListener = new (kafka:DEFAULT_URL, { groupId: "order-group-id", topics: "order-topic", autoSeekOnValidationFailure: false }); service on orderListener { remote function onConsumerRecord(Order[] orders) { // The set of orders received by the service are processed one by one. from Order 'order in orders where 'order.isValid do { log:printInfo(string `Received valid order for ${'order.productName}`); }; } // When an error occurs before the `onConsumerRecord` gets invoked, // `onError` function will get invoked. remote function onError(kafka:Error 'error, kafka:Caller caller) returns error? { // Check whether the `error` is a `kafka:PayloadBindingError` or a `kafka:PayloadValidationError` // and seek past the erroneous record. if 'error is kafka:PayloadBindingError || 'error is kafka:PayloadValidationError { log:printError("Payload error occured", 'error); // The `kafka:PartitionOffset` related to the erroneous record is provided inside // the `kafka:PayloadBindingError`/`kafka:PayloadValidationError`. check caller->seek({ partition: 'error.detail().partition, offset: 'error.detail().offset + 1 }); } else { log:printError("An error occured", 'error); } } } ================================================ FILE: examples/kafka-service-error-handling/kafka_service_error_handling.md ================================================ # Kafka service - Error handling The `kafka:Service` has an `onError` method which is invoked when `kafka:Listener` triggers errors before the `onConsumerRecord` method is invoked. These errors could include payload binding errors or constraint validation errors. In the default behaviour, `kafka:PayloadBindingError`s and `kafka:PayloadValidationError`s are logged to the console and automatically seeked to fetch the next record. To pass these errors to the `onError` method, `autoSeekOnValidationFailure` configuration can be set to `false`. The `onError` method allows the user to handle these errors as needed. If there is no `onError` method in the `kafka:Service`, these are logged to the console with the stack-trace. In addition, `kafka:Service` allows returning errors from the `onConsumerRecord` method. These errors are also logged to the console and the next polling cycle will continue. ::: code kafka_service_error_handling.bal ::: ## Prerequisites - Start a [Kafka broker](https://kafka.apache.org/quickstart) instance. Run the program by executing the following command. ::: out kafka_service_error_handling.out ::: >**Tip:** Run the Kafka client given in the [Kafka producer - Produce message](/learn/by-example/kafka-producer-produce-message) example to produce some messages to the topic. ## Related links - [`kafka` module - API documentation](https://lib.ballerina.io/ballerinax/kafka/latest) - [Kafka service - Specification](https://github.com/ballerina-platform/module-ballerinax-kafka/blob/master/docs/spec/spec.md#432-usage) ================================================ FILE: examples/kafka-service-error-handling/kafka_service_error_handling.metatags ================================================ description: This example demonstrates error handling in Kafka services. keywords: ballerina, ballerina by example, bbe, kafka, consumer, service, asynchronous, error handling ================================================ FILE: examples/kafka-service-error-handling/kafka_service_error_handling.out ================================================ $ bal run kafka_service.bal time = 2023-01-05T12:08:23.938+05:30 level = ERROR module = "" message = "Payload error occured" error = "Data binding failed. If needed, please seek past the record to continue consumption." ================================================ FILE: examples/kafka-service-sasl/kafka_service_sasl.bal ================================================ import ballerinax/kafka; import ballerina/log; type Order readonly & record { int orderId; string productName; decimal price; boolean isValid; }; listener kafka:Listener orderListener = new ("localhost:9093", { groupId: "order-group-id", // Subscribes to the topic `test-kafka-topic`. topics: ["order-topic"], // Provide the relevant authentication configurations to authenticate the consumer // by the `kafka:AuthenticationConfiguration`. auth: { // Provide the authentication mechanism used by the Kafka server. mechanism: kafka:AUTH_SASL_PLAIN, // Username and password should be set here in order to authenticate the consumer. username: "alice", password: "alice@123" }, securityProtocol: kafka:PROTOCOL_SASL_PLAINTEXT }); service on orderListener { remote function onConsumerRecord(Order[] orders) { from Order 'order in orders where 'order.isValid do { log:printInfo(string `Received valid order for ${'order.productName}`); }; } } ================================================ FILE: examples/kafka-service-sasl/kafka_service_sasl.md ================================================ # Kafka service - SASL authentication The `kafka:Service` receives messages from the Kafka server using the `kafka:Listener` via SASL/PLAIN authentication. SASL/PLAIN authentication can be enabled by configuring the `auth`, which requires the authentication mechanism, username, and password. Further, the mode of security must be configured by setting the `securityProtocol` to `kafka:PROTOCOL_SASL_PLAINTEXT`. Use this to connect to a Kafka server secured with SASL/PLAIN. ::: code kafka_service_sasl.bal ::: ## Prerequisites - Start a [Kafka broker](https://kafka.apache.org/quickstart) instance configured to use the [SASL/PLAIN authentication mechanism](https://docs.confluent.io/platform/current/kafka/authentication_sasl/authentication_sasl_plain.html#sasl-plain-overview). Run the program by executing the following command. ::: out kafka_service_sasl.out ::: >**Tip:** Run the Kafka client given in the [Kafka producer - SASL authentication](/learn/by-example/kafka-producer-sasl) example to produce some messages to the topic. ## Related links - [`kafka:AuthenticationConfiguration` record - API documentation](https://lib.ballerina.io/ballerinax/kafka/latest#AuthenticationConfiguration) - [Kafka service SASL authentication - Specification](https://github.com/ballerina-platform/module-ballerinax-kafka/blob/master/docs/spec/spec.md#4312-secure-listener) ================================================ FILE: examples/kafka-service-sasl/kafka_service_sasl.metatags ================================================ description: This example is on how to configure a Kafka listener to use SASL/PLAIN authentication. keywords: ballerina, ballerina by example, bbe, kafka, consumer, listener, service, asynchronous ================================================ FILE: examples/kafka-service-sasl/kafka_service_sasl.out ================================================ $ bal run kafka_service_sasl.bal time = 2022-11-25T14:55:59.366+05:30 level = INFO module = "" message = "Received valid order for Sport shoe" ================================================ FILE: examples/kafka-service-ssl/kafka_service_ssl.bal ================================================ import ballerinax/kafka; import ballerina/log; type Order readonly & record { int orderId; string productName; decimal price; boolean isValid; }; listener kafka:Listener orderListener = new ("localhost:9094", { groupId: "order-group-id", topics: "order-topic", // Provide the relevant secure socket configurations by using `kafka:SecureSocket`. secureSocket: { cert: "./resources/path/to/public.crt", protocol: { // Provide the relevant security protocol. name: kafka:SSL } }, // Provide the type of the security protocol to use in the broker connection. securityProtocol: kafka:PROTOCOL_SSL }); service on orderListener { remote function onConsumerRecord(Order[] orders) { from Order 'order in orders where 'order.isValid do { log:printInfo(string `Received valid order for ${'order.productName}`); }; } } ================================================ FILE: examples/kafka-service-ssl/kafka_service_ssl.md ================================================ # Kafka service - SSL/TLS The `kafka:Service` receives messages from the Kafka server using the `kafka:Listener` via SSL/TLS. SSL/TLS can be enabled by configuring the `secureSocket`, which requires a certificate and the protocol name. Further, the mode of security must be configured by setting the `securityProtocol` to `kafka:PROTOCOL_SSL`. Use this to connect to a Kafka server secured with SSL. ::: code kafka_service_ssl.bal ::: ## Prerequisites - Start a [Kafka broker](https://kafka.apache.org/quickstart) instance configured to use [SSL/TLS](https://docs.confluent.io/3.0.0/kafka/ssl.html#configuring-kafka-brokers). Run the program by executing the following command. ::: out kafka_service_ssl.out ::: >**Tip:** Run the Kafka client given in the [Kafka producer - SSL/TLS](/learn/by-example/kafka-producer-ssl) example to produce some messages to the topic. ## Related links - [`kafka:SecureSocket` record - API documentation](https://lib.ballerina.io/ballerinax/kafka/latest#SecureSocket) - [Kafka secure service - Specification](https://github.com/ballerina-platform/module-ballerinax-kafka/blob/master/docs/spec/spec.md#4312-secure-listener) ================================================ FILE: examples/kafka-service-ssl/kafka_service_ssl.metatags ================================================ description: This example is on how to configure a Kafka listener to use SSL encryption. keywords: ballerina, ballerina by example, bbe, kafka, consumer, listener, service, asynchronous ================================================ FILE: examples/kafka-service-ssl/kafka_service_ssl.out ================================================ $ bal run kafka_service_ssl.bal time = 2022-11-25T14:55:59.366+05:30 level = INFO module = "" message = "Received valid order for Sport shoe" ================================================ FILE: examples/kubernetes-hello-world/Cloud.toml ================================================ [container.image] repository="wso2inc" name="hello" tag="v0.1.0" ================================================ FILE: examples/kubernetes-hello-world/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/kubernetes-hello-world/docker_push.out ================================================ $ docker push wso2inc/hello:v0.1.0 ================================================ FILE: examples/kubernetes-hello-world/execute_curl.out ================================================ $ curl http://192.168.49.2:31360/helloWorld/sayHello Hello from Kubernetes! ================================================ FILE: examples/kubernetes-hello-world/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/kubernetes-hello-world/kubectl_expose.out ================================================ $ kubectl expose deployment hello-deployment --type=NodePort --name=hello-svc-local service/hello-svc-local exposed ================================================ FILE: examples/kubernetes-hello-world/kubectl_pods.out ================================================ $ kubectl get pods NAME READY STATUS RESTARTS AGE wso2-hello-0--deployment-7d4d56457b-7jlzx 1/1 Running 0 57s ================================================ FILE: examples/kubernetes-hello-world/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/kubernetes-hello-world/kubernetes_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 Kubernetes!"; } } ================================================ FILE: examples/kubernetes-hello-world/kubernetes_hello_world.md ================================================ # Kubernetes - Hello world 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 kubernetes_hello_world.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. >**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 ::: 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/kubernetes-hello-world/minikube_ip.out ================================================ $ minikube ip 192.168.49.2 ================================================ FILE: examples/langlib-functions/langlib_functions.bal ================================================ import ballerina/io; public function main() returns error? { // You can call langlib functions using the method-call syntax. string str = "abc".substring(1, 2); // `len` will be 1. int len = str.length(); io:println(len); // `str.length()` is same as `string:length(str)`. len = string:length(str); io:println(len); int val = 123; // The `lang.value` module provides functions that work on values of more than one basic type. // `val.toString()` performs a direct conversion of `val` to string. io:println("value is " + val.toString()); // `val.ensureType()` safely casts a value to a type and // returns an error if the cast is impossible. float|error floatVal = val.ensureType(float); io:println(floatVal); int[] evenNumbers = [2, 4, 6 ,8, 10, 12]; // `value.clone()` returns a clone of a value. int[] clonedEvenNumbers = [2, 4, 6 ,8, 10, 12].clone(); // Following statement is `true`. io:println(evenNumbers == clonedEvenNumbers); // Following statement is `false`. io:println(evenNumbers === clonedEvenNumbers); // `value.cloneReadOnly()` returns a clone of a value that is read-only. int[] & readonly immutableEvenNumbers = evenNumbers.cloneReadOnly(); io:println(immutableEvenNumbers); // `value.cloneWithType()` constructs a value with a specified type by cloning another value. float clonedVal = check val.cloneWithType(float); // Following statement is `true`. io:println(clonedVal); } ================================================ FILE: examples/langlib-functions/langlib_functions.md ================================================ # Langlib functions Langlib is a library defined by the language providing fundamental operations on built-in data types. Langlib functions can be called using the method-call syntax but these types are not objects. There exists a `ballerina/lang.T` module for each built-in type `T` and they are automatically imported using the `T` prefix only if `T` corresponds to a keyword. The `lang.value` module provides functions that work on values of more than one basic type. ::: code langlib_functions.bal ::: ::: out langlib_functions.out ::: ================================================ FILE: examples/langlib-functions/langlib_functions.metatags ================================================ description: This BBE introduces Langlib modules in Ballerina keywords: ballerina, ballerina by example, bbe, langlib, module, Language, Library ================================================ FILE: examples/langlib-functions/langlib_functions.out ================================================ $ bal run langlib_functions.bal 1 1 value is 123 123.0 true false [2,4,6,8,10,12] 123.0 ================================================ FILE: examples/ldap-add-remove-entry/ldap_add_remove_entry.bal ================================================ import ballerina/io; import ballerina/ldap; public function main() returns error? { // Initializes a new LDAP client with credentials. ldap:Client ldapClient = check new ( hostName = "localhost", port = 389, domainName = "cn=admin,dc=example,dc=com", password = "adminpassword" ); // Creates an `ldap:Entry` record for the new entry. ldap:Entry addEntry = { "objectClass": ["top", "person"], "sn": "user", "cn": "user" }; // Adds an entry to the directory server. ldap:LdapResponse addResponse = check ldapClient->add("cn=user,dc=example,dc=com", addEntry); io:println("Add Response: ", addResponse.resultCode); // Deletes an entry from the directory server. ldap:LdapResponse deleteResponse = check ldapClient->delete("cn=user,dc=example,dc=com"); io:println("Delete Response: ", deleteResponse.resultCode); } ================================================ FILE: examples/ldap-add-remove-entry/ldap_add_remove_entry.md ================================================ # LDAP client - Add/Remove entries The `ldap:Client` connects to a directory server and performs various operations on directories. Currently, it supports the generic LDAP operations; `add`, `modify`, `modifyDN`, `compare`, `search`, `searchWithType`, `delete`, and `close`. The `add` operation creates a new entry in a directory server. It requires a `DN` (Distinguished Name) for the entry and the attributes to be included. The `objectClass` attribute must be specified to define the object classes for the entry, and any attributes required by these object classes should also be included. The `delete` operation removes an entry from a directory server. It requires a `DN` (Distinguished Name) of the entry to be deleted. ::: code ldap_add_remove_entry.bal ::: ## Prerequisites - Ensure that an LDAP server is up and running locally on port 389 while running the example. - Run the example by executing the command below. ::: out ldap_add_remove_entry.server.out ::: ## Related links - [`ldap:Client` `add` operation - API documentation](https://central.ballerina.io/ballerina/ldap/latest#Client-add) - [`ldap:Client` `delete` operation - API documentation](https://central.ballerina.io/ballerina/ldap/latest#Client-delete) - [`ldap` module - API documentation](https://lib.ballerina.io/ballerina/ldap/latest/) - [`ldap:Client` - Specification](/spec/ldap/#2-ldap-client) ================================================ FILE: examples/ldap-add-remove-entry/ldap_add_remove_entry.metatags ================================================ description: This example is on how to create/delete entries in a directory server using LDAP in Ballerina. keywords: ballerina, ballerina by example, bbe, ldap, add ================================================ FILE: examples/ldap-add-remove-entry/ldap_add_remove_entry.server.out ================================================ $ bal run ldap_add_remove_entry.bal Add Response: SUCCESS Delete Response: SUCCESS ================================================ FILE: examples/ldap-search-entry/ldap_search_entry.bal ================================================ import ballerina/io; import ballerina/ldap; public function main() returns error? { // Initializes a new LDAP client with credentials. ldap:Client ldapClient = check new ( hostName = "localhost", port = 389, domainName = "cn=admin,dc=example,dc=com", password = "adminpassword" ); // Searches for an entry in the directory server. ldap:SearchResult searchResult = check ldapClient->search( "cn=user,dc=example,dc=com", "(sn=user)", ldap:SUB ); io:println("Search Response: ", searchResult.resultCode); } ================================================ FILE: examples/ldap-search-entry/ldap_search_entry.md ================================================ # LDAP client - Seacrh for an entry The `ldap:Client` connects to a directory server and performs various operations on directories. Currently, it supports the main LDAP operations; `add`, `modify`, `modifyDN`, `compare`, `search`, `searchWithType`, `delete`, and `close`. The `search` operation is used to retrieve entries that match specified criteria. The components of an LDAP search request include: - `baseDN`: Defines the base of the subtree for the search. - `filter`: Specifies the criteria that entries must meet. - `scope`: Determines extent of the the search coverage within the subtree. When a directory server processes a valid search request, it finds entries within the defined scope that meet the filter criteria. The matching entries are then returned to the client in a `ldap:SearchResult` record. ::: code ldap_search_entry.bal ::: ## Prerequisites - Ensure that an LDAP server is up and running locally on port 389 while running the example. - Run the example by executing the command below. ::: out ldap_search_entry.server.out ::: ## Related links - [`ldap:Client` `search` operation - API documentation](https://central.ballerina.io/ballerina/ldap/latest#Client-search) - [`ldap` module - API documentation](https://lib.ballerina.io/ballerina/ldap/latest/) - [`ldap:Client` - Specification](/spec/ldap/#2-ldap-client) ================================================ FILE: examples/ldap-search-entry/ldap_search_entry.metatags ================================================ description: This example is on how to search for an entry in a directory server using LDAP in Ballerina. keywords: ballerina, ballerina by example, bbe, ldap, search ================================================ FILE: examples/ldap-search-entry/ldap_search_entry.server.out ================================================ $ bal run ldap_search_entry.bal Search Response: SUCCESS ================================================ FILE: examples/let-clause/let_clause.bal ================================================ import ballerina/io; type Student record {| string first; string last; int mathematics; int english; |}; public function main() { Student[] students = [ {first: "Melina", last: "Kodel", mathematics: 79, english: 83}, {first: "Tom", last: "Riddle", mathematics: 69, english: 45} ]; int[] names = from var student in students // The `let` clause binds the variables. let int sum = (student.mathematics + student.english) where sum > 0 let int avg = sum / 2 select avg; io:println(names); // The `let` clause supports multiple variable declarations separated by `,`. names = from var student in students let int sum = (student.mathematics + student.english), int avg = sum / 2 where sum > 0 select avg; io:println(names); } ================================================ FILE: examples/let-clause/let_clause.md ================================================ # `let` clause The `let` clause can be used to define temporary variables inside a query expression. It can occur multiple times anywhere between the `from` and `select` clauses. The semantics are similar to `XQuery FLWOR`. ::: code let_clause.bal ::: ::: out let_clause.out ::: ## Related links - [Query expressions](/learn/by-example/query-expressions) - [Sort iterable objects using query](/learn/by-example/sort-iterable-objects) - [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/let-clause/let_clause.metatags ================================================ description: This BBE demonstrates the `let` clause in Ballerina, defining temporary variables inside a query expression, and defining support variables inside a query expression. keywords: ballerina, ballerina by example, bbe, let clause, query, query expression, where, condition ================================================ FILE: examples/let-clause/let_clause.out ================================================ $ bal run let_clause.bal [81,57] [81,57] ================================================ FILE: examples/limit-clause/limit_clause.bal ================================================ import ballerina/io; type Employee record { string firstName; string lastName; decimal salary; }; public function main() { Employee[] employees = [ {firstName: "Jones", lastName: "Welsh", salary: 1000.00}, {firstName: "Anne", lastName: "Frank", salary: 5000.00}, {firstName: "Michael", lastName: "Cain", salary: 10000.00}, {firstName: "Tom", lastName: "Hiddleston", salary: 2000.00} ]; Employee[] top3 = from var e in employees order by e.salary descending // The `limit` clause limits the number of output items to 3. limit 3 select e; foreach var emp in top3 { io:println(emp); } } ================================================ FILE: examples/limit-clause/limit_clause.md ================================================ # `limit` clause The `limit` clause limits the number of results from the earlier clauses. ::: code limit_clause.bal ::: ::: out limit_clause.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/limit-clause/limit_clause.metatags ================================================ description: This BBE demonstrates the `limit` clause in Ballerina, selecting the top `n` elements in a collection, limiting the results to a given number, and selecting a set of elements in a collection. keywords: ballerina, ballerina by example, bbe, limit clause, query, query expression, limit, collection, filter ================================================ FILE: examples/limit-clause/limit_clause.out ================================================ $ bal run limit_clause.bal {"firstName":"Michael","lastName":"Cain","salary":10000.00} {"firstName":"Anne","lastName":"Frank","salary":5000.00} {"firstName":"Tom","lastName":"Hiddleston","salary":2000.00} ================================================ FILE: examples/list-binding-pattern/list_binding_pattern.bal ================================================ import ballerina/io; public function main() { // This will create 2 variables named `id` and `firstname` with the types // `int` and `string`. The string value `"Doe"` returned from `getDetails()` // is ignored using `_`. [int, [string, string]] [id, [firstname, _]] = getDetails(); io:println(id); io:println(firstname); } function getDetails() returns [int, [string, string]] { return [1234, ["John", "Doe"]]; } ================================================ FILE: examples/list-binding-pattern/list_binding_pattern.md ================================================ # List binding pattern A list binding pattern binds members of a list to variables. It can contain more than one binding pattern. The list binding pattern can be employed with both tuples and arrays. ::: code list_binding_pattern.bal ::: ::: out list_binding_pattern.out ::: ## Related links - [Rest binding pattern in list binding pattern](/learn/by-example/rest-binding-pattern-in-list-binding-pattern/) - [Binding patterns](/learn/by-example/binding-patterns/) - [Arrays](/learn/by-example/arrays/) - [Tuples](/learn/by-example/tuples/) ================================================ FILE: examples/list-binding-pattern/list_binding_pattern.metatags ================================================ description: This BBE introduces the use of the list binding pattern to bind members of a list, array, or tuple using more than one binding pattern and destructuring a list. keywords: ballerina, ballerina by example, bbe, binding pattern, list binding pattern, array destructuring, list destructuring, tuple destructuring ================================================ FILE: examples/list-binding-pattern/list_binding_pattern.out ================================================ $ bal run list_binding_pattern.bal 1234 John ================================================ FILE: examples/list-binding-pattern-in-match-statement/list_binding_pattern_in_match_statement.bal ================================================ import ballerina/io; function matchCommand(any commands) { match commands { var [show] => { io:println(show); } // The list binding pattern below binds lists that contain three list items // where the third element in the list is the boolean value `true`. var [remove, all, isDir] if isDir is true => { io:println(remove, " directories ", all); } // The list binding pattern below binds lists that contain three list items. var [remove, all, _] => { io:println(remove, " files ", all); } // The list binding pattern below binds lists that contain two list items, // in which the second list item is also a list of two items. var [copy, [file1, file2]] => { io:println(copy, " ", file1, " into ", file2); } _ => { io:println("Unrecognized command."); } } } public function main() { matchCommand(["Show"]); matchCommand(["Remove", "*", true]); matchCommand(["Remove", "f.txt", false]); matchCommand(["Copy", ["a.bal", "b.bal"]]); matchCommand(1); } ================================================ FILE: examples/list-binding-pattern-in-match-statement/list_binding_pattern_in_match_statement.md ================================================ # List binding pattern in match statement List binding patterns can be used in a match statement to bind parts of successfully matched list items to variables. The rest binding pattern (`...r`) can be used in the list binding pattern to bind the list items that are not explicitly bound in the mapping binding pattern. ::: code list_binding_pattern_in_match_statement.bal ::: ::: out list_binding_pattern_in_match_statement.out ::: ## Related links - [List binding pattern](/learn/by-example/list-binding-pattern/) - [Rest binding pattern in list binding pattern](/learn/by-example/rest-binding-pattern-in-list-binding-pattern/) - [Binding patterns](/learn/by-example/binding-patterns/) - [Match statement](/learn/by-example/match-statement/) ================================================ FILE: examples/list-binding-pattern-in-match-statement/list_binding_pattern_in_match_statement.metatags ================================================ description: This BBE introduces the use of a list binding pattern to match the list items in a match statement. keywords: ballerina, ballerina by example, bbe, binding pattern, list binding pattern, match statement, list binding pattern in match statement ================================================ FILE: examples/list-binding-pattern-in-match-statement/list_binding_pattern_in_match_statement.out ================================================ $ bal run list_binding_pattern_in_match_statement.bal Show Remove directories * Remove files f.txt Copy a.bal into b.bal Unrecognized command. ================================================ FILE: examples/list-equality/list_equality.bal ================================================ import ballerina/io; public function main() { int[] a = [1]; int[1] b = [1]; // Output will be `true` as the values are the same. io:println(a == b); // Output will be `false` as the references are different. io:println(a === b); // Output will be `true` as the references are the same. io:println(a === a); // This will assign the reference `a` to `c`. int[] c = a; // Output will be `true` as the references are the same. io:println(a === c); string[3][2] orderItems = [["carrot", "apple"], ["avacado", "egg"], ["fish", "banana"]]; string[3][2] orderItems2 = [["carrot", "apple"], ["avacado", "egg"], ["fish", "banana"]]; // Output will be `true` as the order items are the same. io:println(orderItems == orderItems2); // Output will be `false` as the order items are the same. io:println(orderItems != orderItems2); // Output will be `true` as the references are different. io:println(orderItems !== orderItems2); string[3][2] orderItems3 = [["avacado", "egg"], ["carrot", "apple"], ["fish", "banana"]]; // Output will be `false` as the order of the items are different. io:println(orderItems == orderItems3); } ================================================ FILE: examples/list-equality/list_equality.md ================================================ # List equality You can use `==` and `!=` on lists to check the deep equality of two lists: two lists are deep equal if they have the same members in the same order. Deep equality only works for `anydata` lists. `===` and `!==` check for the exact equality, which matches the references of the lists. ::: code list_equality.bal ::: ::: out list_equality.out ::: ## Related links - [Tuples](/learn/by-example/tuples) - [Arrays](/learn/by-example/arrays) - [Expression equality](/learn/by-example/expression-equality) - [List sub typing](/learn/by-example/list-subtyping) ================================================ FILE: examples/list-equality/list_equality.metatags ================================================ description: Check two list are equal, check two collections are equal, check two lists have the same members, Check two list are the same reference, Compare two lists keywords: ballerina, ballerina by example, bbe, ==, !=, ===, !==, equality, array, deep, exact ================================================ FILE: examples/list-equality/list_equality.out ================================================ $ bal run list_equality.bal true false true true true false true false ================================================ FILE: examples/list-subtyping/list_subtyping.bal ================================================ import ballerina/io; public function main() { int[3] numbers = [144, 232, 322]; // `numbers`, of type `int[3]`, will be a subtype of `int[]` // since `T[n]` is a subtype of `T[]` io:println(numbers is int[]); byte[3] numbers2 = [1, 2, 3]; // `numbers2`, of type byte[3], will be a subtype of `int[3]` // since `byte` is a subtype of `int` and lengths are the same io:println(numbers2 is int[3]); // `numbers2`, of type byte[3], will be a subtype of `int[]` // since `byte` is a subtype of `int` and `T[n]` is a subtype of `T[]` io:println(numbers2 is int[]); [byte, string] person = [1, "Mike"]; // `[byte, string]` is a subtype of `[int, anydata]` io:println(person is [int, anydata]); // `[byte, string]` is a subtype of `[int, anydata...]` io:println(person is [int, anydata...]); // `int[3]` is a subtype of `[int, anydata...]` io:println(numbers is [int, anydata...]); } ================================================ FILE: examples/list-subtyping/list_subtyping.md ================================================ # List subtyping An array with a length is a subtype of an array of same member type descriptor without a length. Two arrays with the same length or without a length is a subtype of the other if the member type descriptor is a subtype of the other. Tuple type `T` is a subtype of type `T1` if the set of values allowed in `T` is a sub set of values allowed in `T1` ::: code list_subtyping.bal ::: ::: out list_subtyping.out ::: ## Related links - [Tuples](/learn/by-example/tuples) - [Arrays](/learn/by-example/arrays) - [List equality](/learn/by-example/list-equality) ================================================ FILE: examples/list-subtyping/list_subtyping.metatags ================================================ description: Check one list is a subset of another, Check one list can contains another list, Check one array is sub type of another keywords: ballerina, ballerina by example, bbe, array, collection, tuple, sub type ================================================ FILE: examples/list-subtyping/list_subtyping.out ================================================ $ bal run list_subtyping.bal true true true true true true ================================================ FILE: examples/lock-statement/lock_statement.bal ================================================ import ballerina/io; int n = 0; function inc() { // Locks the global variable `n` and increments it by 1. lock { n += 1; } io:println(n); } public function main() { inc(); } ================================================ FILE: examples/lock-statement/lock_statement.md ================================================ # Lock statement The `lock` statement allows mutable state to be safely accessed from multiple strands that are running on separate threads. Semantics are like an atomic section. The execution of the outermost `lock` block is not interleaved. Naive implementation uses single, global, recursive lock. Efficient implementation can do compile-time lock inference. ::: code lock_statement.bal ::: Executing the above code gives the output below. ::: out lock_statement.out ::: ================================================ FILE: examples/lock-statement/lock_statement.metatags ================================================ description: This BBE introduces lock statement in Ballerina. keywords: ballerina, ballerina by example, bbe, lock statement ================================================ FILE: examples/lock-statement/lock_statement.out ================================================ $ bal run lock_statement.bal 1 ================================================ FILE: examples/log-file-rotation/app_log_rotation.out ================================================ $ ls -la logs/ total 16 drwxr-xr-x@ 4 wso2 staff 128 Jan 5 21:20 . drwxr-xr-x@ 9 wso2 staff 288 Jan 5 21:20 .. -rw-r--r--@ 1 wso2 staff 2325 Jan 5 21:20 app-20260105-212048.log -rw-r--r--@ 1 wso2 staff 248 Jan 5 21:20 app.log $ cat logs/app-*.log time=2026-01-05T21:20:42.771+05:30 level=INFO module="" message="Starting log rotation demonstration" time=2026-01-05T21:20:42.801+05:30 level=INFO module="" message="Application started" version="1.0.0" time=2026-01-05T21:20:42.804+05:30 level=INFO module="" message="Processing request" requestId="req1" endpoint="/api/users" responseTime=126 time=2026-01-05T21:20:42.806+05:30 level=INFO module="" message="Processing request" requestId="req2" endpoint="/api/users" responseTime=127 time=2026-01-05T21:20:42.808+05:30 level=INFO module="" message="Processing request" requestId="req3" endpoint="/api/users" responseTime=128 time=2026-01-05T21:20:42.810+05:30 level=INFO module="" message="Processing request" requestId="req4" endpoint="/api/users" responseTime=129 time=2026-01-05T21:20:42.811+05:30 level=INFO module="" message="Processing request" requestId="req5" endpoint="/api/users" responseTime=130 time=2026-01-05T21:20:44.815+05:30 level=INFO module="" message="Processing request" requestId="req6" endpoint="/api/users" responseTime=131 time=2026-01-05T21:20:44.816+05:30 level=INFO module="" message="Processing request" requestId="req7" endpoint="/api/users" responseTime=132 time=2026-01-05T21:20:44.818+05:30 level=INFO module="" message="Processing request" requestId="req8" endpoint="/api/users" responseTime=133 time=2026-01-05T21:20:44.819+05:30 level=INFO module="" message="Processing request" requestId="req9" endpoint="/api/users" responseTime=134 time=2026-01-05T21:20:44.820+05:30 level=INFO module="" message="Processing request" requestId="req10" endpoint="/api/users" responseTime=135 time=2026-01-05T21:20:46.823+05:30 level=INFO module="" message="Processing request" requestId="req11" endpoint="/api/users" responseTime=136 time=2026-01-05T21:20:46.824+05:30 level=INFO module="" message="Processing request" requestId="req12" endpoint="/api/users" responseTime=137 time=2026-01-05T21:20:46.825+05:30 level=INFO module="" message="Processing request" requestId="req13" endpoint="/api/users" responseTime=138 time=2026-01-05T21:20:46.826+05:30 level=INFO module="" message="Processing request" requestId="req14" endpoint="/api/users" responseTime=139 time=2026-01-05T21:20:46.827+05:30 level=INFO module="" message="Processing request" requestId="req15" endpoint="/api/users" responseTime=140 ================================================ FILE: examples/log-file-rotation/log_file_rotation.bal ================================================ import ballerina/log; import ballerina/lang.runtime; public function main() { // Root logger is configured in Config.toml with TIME_BASED rotation // Logs will be written to ./logs/app.log log:printInfo("Starting log rotation demonstration"); log:printInfo("Application started", version = "1.0.0"); foreach int i in 1...15 { log:printInfo("Processing request", requestId = string `req${i}`, endpoint = "/api/users", responseTime = 125 + i); if i % 5 == 0 { // Sleep to demonstrate time-based rotation (5 second threshold) runtime:sleep(2); } } log:printInfo("Application processing completed"); log:printInfo("Log rotation demonstration complete. Check ./logs directory for rotated log files."); } ================================================ FILE: examples/log-file-rotation/log_file_rotation.md ================================================ # Log file rotation Log file rotation helps manage log file sizes by automatically creating backups when certain conditions are met, preventing disk space issues. This example demonstrates TIME_BASED rotation configured through `Config.toml`. ## Configuring File Rotation The root logger is configured in `Config.toml` with TIME_BASED rotation. All logs using the default logger will automatically benefit from this rotation policy. ::: code Config.toml ::: ::: code log_file_rotation.bal ::: Run the example to see rotation in action: ::: out log_file_rotation.out ::: ## How Rotation Works When a log file rotates, it's renamed with a timestamp suffix and a new file is created: ``` logs/ app.log (current log file) app-20251223-225602.log (rotated backup) app-20251223-223015.log (older backup) ``` Older backups beyond `maxBackupFiles` are automatically deleted. ### Application Log Rotation Output ::: out app_log_rotation.out ::: ## Configuration Options Rotation policies support these configuration parameters: | Parameter | Default | Description | |-----------|---------|-------------| | `policy` | `"BOTH"` | Rotation trigger: `"SIZE_BASED"`, `"TIME_BASED"`, or `"BOTH"` | | `maxFileSize` | 10485760 | Maximum file size in bytes (10MB default) | | `maxAge` | 86400 | Maximum file age in seconds (24 hours default) | | `maxBackupFiles` | 10 | Number of rotated backup files to retain | > **Note:** This example uses a small threshold (5 seconds) to make rotation observable during demonstration. In production, use realistic values such as `maxAge = 86400` (24 hours). ## Related links - [`log` module - Specification](https://ballerina.io/spec/log/#35-configure-log-rotation) - [`log` module - API documentation](https://lib.ballerina.io/ballerina/log/latest/) ================================================ FILE: examples/log-file-rotation/log_file_rotation.metatags ================================================ description: BBE on how to configure log file rotation in Ballerina. keywords: ballerina, ballerina by examples, bbe, log, rotation, file rotation, log management, size based, time based ================================================ FILE: examples/log-file-rotation/log_file_rotation.out ================================================ $ bal run log_file_rotation.bal ================================================ FILE: examples/logger-from-config/audit_logs.out ================================================ {"time":"2026-01-05T21:22:17.164+05:30", "level":"INFO", "module":"", "message":"User action recorded", "action":"login", "userId":"alice123", "env":"prod", "nodeId":"server-001", "component":"audit", "compliance":"SOX"} {"time":"2026-01-05T21:22:17.176+05:30", "level":"INFO", "module":"", "message":"User action recorded", "action":"file_access", "userId":"bob456", "env":"prod", "nodeId":"server-001", "component":"audit", "compliance":"SOX"} ================================================ FILE: examples/logger-from-config/logger_from_config.bal ================================================ import ballerina/log; // Define specialized logger configurations for different purposes final readonly & log:Config auditConfig = { level: log:INFO, format: log:JSON_FORMAT, destinations: [{ path: "./logs/audit.log", rotation: { policy: log:SIZE_BASED, maxFileSize: 10485760, // 10MB maxBackupFiles: 10 } }], keyValues: {"component": "audit", "compliance": "SOX"} }; final readonly & log:Config metricsConfig = { level: log:DEBUG, format: log:LOGFMT, destinations: [{ path: "./logs/metrics.log", rotation: { policy: log:TIME_BASED, maxAge: 86400, // 24 hours maxBackupFiles: 7 } }], keyValues: {"component": "metrics", "retention": "30days"} }; function processAuditEvent(string action, string userId) returns error? { // Create audit logger from configuration log:Logger auditLogger = check log:fromConfig(auditConfig); // Audit logs automatically include the component and compliance context auditLogger.printInfo("User action recorded", action = action, userId = userId); } function recordMetrics(string metricName, decimal value, string unit) returns error? { // Create metrics logger from configuration log:Logger metricsLogger = check log:fromConfig(metricsConfig); // Add operation-specific context to metrics logger log:Logger operationLogger = check metricsLogger.withContext(operation = "performance_monitoring"); operationLogger.printDebug("Recording performance metric"); operationLogger.printInfo("Performance metric recorded", metric = metricName, value = value, unit = unit); } public function main() returns error? { // Regular application logs using root logger log:printInfo("Application started", version = "1.2.0"); // Use specialized loggers with unique configurations check processAuditEvent("login", "alice123"); check processAuditEvent("file_access", "bob456"); check recordMetrics("response_time", 245.5, "ms"); check recordMetrics("memory_usage", 78.2, "percent"); log:printInfo("Application processing completed"); } ================================================ FILE: examples/logger-from-config/logger_from_config.md ================================================ # Logger from configuration This example demonstrates how to create specialized loggers with unique configurations. Each logger can have its own format, destinations, log level, file rotation policy, and default context. ::: code logger_from_config.bal ::: The example creates two specialized loggers: - **Audit logger**: Uses SIZE_BASED rotation (10MB file size limit) with JSON format - **Metrics logger**: Uses TIME_BASED rotation (24-hour age limit) with LOGFMT format > **Note:** All loggers created from the configuration automatically inherit the default context of the root logger. ::: code Config.toml ::: Terminal output: ::: out logger_from_config.out ::: Audit logs: ::: out audit_logs.out ::: Metrics logs: ::: out metrics_logs.out ::: Notice how each logger type produces different output but all share the default context. Each logger can also have its own rotation policy - the audit logger rotates based on file size (ideal for high-volume logs), while the metrics logger rotates based on time (ideal for daily rollover). This pattern is ideal for applications that need different logging behaviors for different concerns (audit trails, performance monitoring, security events, etc.). ## Related links - [`log` module - Specification](https://ballerina.io/spec/log/#432-loggers-with-unique-logging-configuration) - [`log` module - API documentation](https://lib.ballerina.io/ballerina/log/latest) ================================================ FILE: examples/logger-from-config/logger_from_config.metatags ================================================ description: BBE on how to create specialized loggers with unique configurations. keywords: ballerina, ballerina by examples, bbe, log, fromConfig, configuration, specialized logger ================================================ FILE: examples/logger-from-config/logger_from_config.out ================================================ $ bal run logger_from_config.bal time=2026-01-05T21:22:17.128+05:30 level=INFO module="" message="Application started" version="1.2.0" env="prod" nodeId="server-001" time=2026-01-05T21:22:17.188+05:30 level=INFO module="" message="Application processing completed" env="prod" nodeId="server-001" ================================================ FILE: examples/logger-from-config/metrics_logs.out ================================================ time=2026-01-05T21:22:17.179+05:30 level=DEBUG module="" message="Recording performance metric" env="prod" nodeId="server-001" component="metrics" retention="30days" operation="performance_monitoring" time=2026-01-05T21:22:17.183+05:30 level=INFO module="" message="Performance metric recorded" metric="response_time" value=245.5 unit="ms" env="prod" nodeId="server-001" component="metrics" retention="30days" operation="performance_monitoring" time=2026-01-05T21:22:17.185+05:30 level=DEBUG module="" message="Recording performance metric" env="prod" nodeId="server-001" component="metrics" retention="30days" operation="performance_monitoring" time=2026-01-05T21:22:17.187+05:30 level=INFO module="" message="Performance metric recorded" metric="memory_usage" value=78.2 unit="percent" env="prod" nodeId="server-001" component="metrics" retention="30days" operation="performance_monitoring" ================================================ FILE: examples/logging/logging.bal ================================================ import ballerina/log; public function main() { // Log messages at different levels log:printDebug("This is a debug message"); log:printInfo("Application started successfully"); log:printWarn("This is a warning message"); log:printError("This is an error message"); } ================================================ FILE: examples/logging/logging.md ================================================ # Logging This example demonstrates how to log messages at different levels in Ballerina. The log module provides four log levels in order of priority: ERROR, WARN, INFO, and DEBUG. ::: code logging.bal ::: By default, only INFO and higher level logs (INFO, WARN, ERROR) are displayed. DEBUG logs are filtered out unless explicitly configured. ::: out logging.out ::: > **Note:** The DEBUG message is not shown in the output because the default log level is INFO. ## 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/logging/logging.metatags ================================================ description: BBE on how to log messages in Ballerina at different log levels including 'DEBUG', 'ERROR', 'INFO', and 'WARN'. keywords: ballerina, ballerina by examples, bbe, log, level, debug, info, warn, error ================================================ FILE: examples/logging/logging.out ================================================ $ bal run logging.bal time=2025-08-25T18:08:00.546+05:30 level=INFO module="" message="Application started successfully" time=2025-08-25T18:08:00.563+05:30 level=WARN module="" message="This is a warning message" time=2025-08-25T18:08:00.564+05:30 level=ERROR module="" message="This is an error message" ================================================ FILE: examples/logging/tests/log_api_test.bal ================================================ import ballerina/test; import ballerina/log; log:PrintableRawTemplate|string printDebug = ""; log:PrintableRawTemplate|string printError = ""; log:PrintableRawTemplate|string printInfo = ""; log:PrintableRawTemplate|string printWarn = ""; // This is the mock function, which will replace the real function. @test:Mock { moduleName: "ballerina/log", functionName: "printDebug" } test:MockFunction mock_printDebug = new(); public function mockPrintDebug(string|log:PrintableRawTemplate msg, error? err = (), error:StackFrame[]? stackTrace = (), *log:KeyValues keyValues) { printDebug = msg; } @test:Mock { moduleName: "ballerina/log", functionName: "printError" } test:MockFunction mock_printError = new(); public function mockPrintError(string|log:PrintableRawTemplate msg, error? err = (), error:StackFrame[]? stackTrace = (), *log:KeyValues keyValues) { printError = msg; } @test:Mock { moduleName: "ballerina/log", functionName: "printInfo" } test:MockFunction mock_printInfo = new(); public function mockPrintInfo(string|log:PrintableRawTemplate msg, error? err = (), error:StackFrame[]? stackTrace = (), *log:KeyValues keyValues) { printInfo = msg; } @test:Mock { moduleName: "ballerina/log", functionName: "printWarn" } test:MockFunction mock_printWarn = new(); public function mockPrintWarn(string|log:PrintableRawTemplate msg, error? err = (), error:StackFrame[]? stackTrace = (), *log:KeyValues keyValues) { printWarn = msg; } @test:Config {} function testFunc() { test:when(mock_printDebug).call("mockPrintDebug"); test:when(mock_printError).call("mockPrintError"); test:when(mock_printInfo).call("mockPrintInfo"); test:when(mock_printWarn).call("mockPrintWarn"); // Invoking the main function. main(); test:assertEquals(printDebug, "This is a debug message"); test:assertEquals(printError, "This is an error message"); test:assertEquals(printInfo, "Application started successfully"); test:assertEquals(printWarn, "This is a warning message"); } ================================================ FILE: examples/logging-configuration/ModuleConfig.toml ================================================ [[ballerina.log.modules]] name = "[ORG_NAME]/[MODULE_NAME]" level = "[LOG_LEVEL]" ================================================ FILE: examples/logging-configuration/logging_configuration.bal ================================================ import ballerina/log; public function main() { // These log messages will be formatted and filtered according to Config.toml log:printDebug("Debug message - application initialization"); log:printInfo("Application started", version = "1.0.0"); log:printWarn("High memory usage detected", memoryUsage = "85%"); log:printError("Failed to connect to external service", serviceName = "PaymentAPI", timeout = "30s"); // Logs will include the root context configured in Config.toml log:printInfo("User session created", userId = "user123", sessionId = "sess-456"); } ================================================ FILE: examples/logging-configuration/logging_configuration.md ================================================ # Log configuration This example demonstrates how to configure logging behavior using the `Config.toml` file. You can control the log level, format, output destinations, file rotation, and add root context that appears in all log messages. ::: code Config.toml ::: The configuration above sets: - **level**: `DEBUG` to show all log levels. This is set to the root logger, affecting all modules unless overridden - **format**: `json` for structured JSON output - **destinations**: Logs to a file with automatic rotation - **rotation**: TIME_BASED policy to rotate log files based on age - `maxAge`: Maximum age of log files in seconds (5 seconds for demo) - `maxBackupFiles`: Number of rotated backup files to retain - **keyValues**: Root context added to all logs ::: code logging_configuration.bal ::: When you run the application with this configuration, all log messages will be in JSON format and include the root context. The log messages will be written to the specified file (`./logs/app.log`) and will automatically rotate based on the configured policy. > **Note:** Ensure that the application has write permissions to the specified log file path. ::: out logging_configuration.out ::: > **Tip:** Each module can also be assigned its own log level. To assign a log level to a module, provide the following entry in the `Config.toml` file: ::: code ModuleConfig.toml ::: ## Related links - [`log` module - Specification](https://ballerina.io/spec/log/#3-configure-logging) - [`log` module - API documentation](https://lib.ballerina.io/ballerina/log/latest) ================================================ FILE: examples/logging-configuration/logging_configuration.metatags ================================================ description: BBE on how to configure logging behavior using Config.toml including log level, format, destinations, and root context. keywords: ballerina, ballerina by examples, bbe, log, configuration, config.toml, format, level ================================================ FILE: examples/logging-configuration/logging_configuration.out ================================================ {"time":"2025-08-25T18:13:34.598+05:30", "level":"DEBUG", "module":"", "message":"Debug message - application initialization", "env":"prod", "nodeId":"server-001"} {"time":"2025-08-25T18:13:34.615+05:30", "level":"INFO", "module":"", "message":"Application started", "version":"1.0.0", "env":"prod", "nodeId":"server-001"} {"time":"2025-08-25T18:13:34.617+05:30", "level":"WARN", "module":"", "message":"High memory usage detected", "memoryUsage":"85%", "env":"prod", "nodeId":"server-001"} {"time":"2025-08-25T18:13:34.618+05:30", "level":"ERROR", "module":"", "message":"Failed to connect to external service", "serviceName":"PaymentAPI", "timeout":"30s", "env":"prod", "nodeId":"server-001"} {"time":"2025-08-25T18:13:34.618+05:30", "level":"INFO", "module":"", "message":"User session created", "userId":"user123", "sessionId":"sess-456", "env":"prod", "nodeId":"server-001"} ================================================ FILE: examples/logging-with-context/logging_with_context.bal ================================================ import ballerina/log; import ballerina/time; public type User record {| string name; int age; |}; public function main() { // Log with key-value pairs for additional context log:printInfo("User authentication attempt", userId = "john123", sessionId = "abc-def-ghi"); // Log with different data types log:printInfo("Processing order", orderId = 845315, amount = 99.99, items = 3, success = true); // Log with structured data User user = {name: "Alice", age: 25}; log:printInfo("User profile loaded", user = user); // Log with function pointers for dynamic values log:printInfo("System status", current_time = isolated function() returns string { return time:utcToString(time:utcNow()); }); // Log with templates for formatted context string productName = "Laptop"; int quantity = 5; log:printInfo(`Processing inventory update`, details = `Product: ${productName}, Quantity: ${quantity}`); } ================================================ FILE: examples/logging-with-context/logging_with_context.md ================================================ # Logging with context This example demonstrates how to add contextual information to log messages using key-value pairs. This helps in debugging and monitoring by providing additional metadata with each log entry. ::: code logging_with_context.bal ::: The key-value pairs can include various data types such as strings, numbers, booleans, records, function pointers, and templates. This contextual information makes logs more informative and easier to analyze. ::: out logging_with_context.out ::: Key-value pairs appear as additional fields in the log output, making it easy to filter and search logs based on specific criteria. ## 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/logging-with-context/logging_with_context.metatags ================================================ description: BBE on how to add contextual information to log messages using key-value pairs in Ballerina. keywords: ballerina, ballerina by examples, bbe, log, context, key-value, metadata ================================================ FILE: examples/logging-with-context/logging_with_context.out ================================================ $ bal run logging_with_context.bal time=2025-08-25T18:20:21.418+05:30 level=INFO module="" message="User authentication attempt" userId="john123" sessionId="abc-def-ghi" time=2025-08-25T18:20:21.433+05:30 level=INFO module="" message="Processing order" orderId=845315 amount=99.99 items=3 success=true time=2025-08-25T18:20:21.436+05:30 level=INFO module="" message="User profile loaded" user={"name":"Alice","age":25} time=2025-08-25T18:20:21.438+05:30 level=INFO module="" message="System status" current_time="2025-08-25T12:50:21.440249Z" time=2025-08-25T18:20:21.449+05:30 level=INFO module="" message="Processing inventory update" details="Product: Laptop, Quantity: 5" ================================================ FILE: examples/main-function/main_function.bal ================================================ import ballerina/io; // The `main` function can accept command-line arguments and return `error` or `nil`. public function main(int value) returns error? { io:println(value); if value >= 3 { return error("Input should less than 3"); } } ================================================ FILE: examples/main-function/main_function.md ================================================ # Main function The `main` function is the program entry point and the `public` keyword makes this function visible outside the module. It is a compile-time error if visibility is not set to `public` in the `main` function. The `main` function can accept the command-line arguments and returns `error` or nil. ::: code main_function.bal ::: ::: out main_function.out ::: ## Related links - [Functions](/learn/by-example/functions/) ================================================ FILE: examples/main-function/main_function.metatags ================================================ description: This BBE demonstrates passing comand-line arguments to the main function, returning errors from the main function, and constructing custom errors in Ballerina. keywords: ballerina, ballerina by example, bbe, main function, error, comand-line arguments ================================================ FILE: examples/main-function/main_function.out ================================================ $ bal run main_function.bal -- 5 5 error: Input should less than 3 ================================================ FILE: examples/manage-scheduled-jobs/manage_scheduled_jobs.bal ================================================ import ballerina/io; import ballerina/lang.runtime; import ballerina/task; import ballerina/time; // Creates a job to be executed by the scheduler. class Job { *task:Job; int i = 1; string jobIdentifier; // Executes this function when the scheduled trigger fires. public function execute() { self.i += 1; io:println(self.jobIdentifier + ", MyCounter: ", self.i); } isolated function init(int i, string jobIdentifier) { self.i = i; self.jobIdentifier = jobIdentifier; } } public function main() returns error? { // Gets the current time. time:Utc currentUtc = time:utcNow(); // Increases the time by three seconds to set the starting delay for the scheduling job. time:Utc newTime = time:utcAddSeconds(currentUtc, 5); // Gets the `time:Civil` for the given time. time:Civil time = time:utcToCivil(newTime); // Schedules the tasks to execute the job every second. task:JobId id1 = check task:scheduleJobRecurByFrequency( new Job(0, "1st Job"), 1); task:JobId id2 = check task:scheduleJobRecurByFrequency( new Job(0, "2nd Job"), 3); // Schedules the one-time job at the specified time. _ = check task:scheduleOneTimeJob(new Job(0, "3rd Job"), time); // Waits for 3 seconds. runtime:sleep(3); // Gets all the running jobs. task:JobId[] result = task:getRunningJobs(); io:println("No of running jobs: ", result.length()); // Pauses the specified job. check task:pauseJob(id1); io:println("Pasused the 1st job."); // Waits for 3 seconds. runtime:sleep(3); // Resumes the specified job. check task:resumeJob(id1); io:println("Resumed the 1st job."); // Gets all the running jobs. result = task:getRunningJobs(); io:println("No of running jobs: ", result.length()); // Waits for 3 seconds. runtime:sleep(3); // Unschedules the jobs. check task:unscheduleJob(id1); check task:unscheduleJob(id2); } ================================================ FILE: examples/manage-scheduled-jobs/manage_scheduled_jobs.md ================================================ # Manage sheduled jobs The `task` library provides functions to manage the scheduled jobs such as pause, resume, unscheduled, etc. For more information on the underlying module, see the [`task` module](https://lib.ballerina.io/ballerina/task/latest/). ::: code manage_scheduled_jobs.bal ::: To run this sample, use the `bal run` command. ::: out manage_scheduled_jobs.out ::: ================================================ FILE: examples/manage-scheduled-jobs/manage_scheduled_jobs.metatags ================================================ description: This BBE shows how to manage the scheduled jobs in Ballerina. keywords: ballerina, ballerina by example, BBE, task, job, scheduler ================================================ FILE: examples/manage-scheduled-jobs/manage_scheduled_jobs.out ================================================ $ bal run manage_scheduled_jobs.bal 1st Job, MyCounter: 1 2nd Job, MyCounter: 1 1st Job, MyCounter: 2 1st Job, MyCounter: 3 1st Job, MyCounter: 4 2nd Job, MyCounter: 2 No of running jobs: 3 Pasused the 1st job. 3rd Job, MyCounter: 1 2nd Job, MyCounter: 3 Resumed the 1st job. 1st Job, MyCounter: 5 1st Job, MyCounter: 6 No of running jobs: 2 1st Job, MyCounter: 7 1st Job, MyCounter: 8 1st Job, MyCounter: 9 1st Job, MyCounter: 10 2nd Job, MyCounter: 4 ================================================ FILE: examples/mapping-binding-pattern/mapping_binding_pattern.bal ================================================ import ballerina/io; type Person record {| int id; string fname; string lname; |}; public function main() { // `_` is used to ignore the value of the `id` field. // The values of the `fname` and `lname` fields are bound to the `firstName` and // `lastName` variable names. Person {id: _, fname: firstName, lname: lastName} = getPerson(); io:println(firstName + " " + lastName); // The `fname` and `lname` fields do not have explicitly defined variable names. // `{fname, lname}` is the same as `{fname: fname, lname: lname}`. // The `id` field is ignored as it is not bound to a variable. Person {fname, lname} = getPerson(); io:println(fname + " " + lname); string givenName; string surname; // This destructures and assigns the values of the fields in the destructed record // to the variable references. // The values of the `fname` and `lname` fields are assigned to the `givenName` and // `surname` variables. {fname: givenName, lname: surname} = getPerson(); io:println(givenName + " " + surname); } function getPerson() returns Person { Person person = {id: 1001, fname: "Anne", lname: "Frank"}; return person; } ================================================ FILE: examples/mapping-binding-pattern/mapping_binding_pattern.md ================================================ # Mapping binding pattern A mapping binding pattern matches a mapping value with its fields. A `record` type will be assigned to the mapping binding pattern if it successfully matches the value. Both the `map` and `record` types can be used in mapping binding patterns. Record destructuring in binding patterns can be used to refer to existing variables as a record, destructure the given value on the right-hand side, and assign the values to each individual variable of the record during the runtime. These binding patterns are especially helpful when dealing with queries and are useful when destructuring JSON values. ::: code mapping_binding_pattern.bal ::: ::: out mapping_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 mapping binding pattern](/learn/by-example/rest-binding-pattern-in-mapping-binding-pattern/) ================================================ FILE: examples/mapping-binding-pattern/mapping_binding_pattern.metatags ================================================ description: This BBE demonstrates the mapping binding pattern, record typed binding, record destructuring with binding patterns, and JSON destructuring. keywords: ballerina, ballerina by example, bbe, binding pattern, mapping binding pattern, mapping binding, record typed binding, record binding, record destructuring, json destructuring ================================================ FILE: examples/mapping-binding-pattern/mapping_binding_pattern.out ================================================ $ bal run mapping_binding_pattern.bal Anne Frank Anne Frank Anne Frank ================================================ FILE: examples/mapping-binding-pattern-in-match-statement/mapping_binding_pattern_in_match_statement.bal ================================================ import ballerina/io; type Time record { int hours; int minutes; }; type Day record { Time t; }; function matchTime1(Time pair) { match pair { // The binding pattern below matches the mappings that contain at least the fields with // the `hours` and `seconds` keys. The values of these fields can be accessed via the // `hours` and `seconds` variables within this block. var {hours, minutes} => { io:println(hours, ", ", minutes); } } } function matchTime2(Time time) { match time { // The binding pattern below has a rest binding pattern to capture the additional // fields that may be specified in the open record value assigned to the `time` variable. // Type of the `rest` variable can be considered a map of `anydata`. However, it cannot // contain the `hours` or `minutes` keys. var {hours, minutes, ...rest} => { io:println(hours, ", ", minutes, ", ", rest); } } } function matchTime3(Day day) { match day { // The pattern below matches a mapping that has a field with the `t`key // and a value, which is another mapping that contains at least the fields // with the `hours` and `minutes` keys. var {t: {hours, minutes}} => { io:println(hours, ", ", minutes); } } } function matchTime4(Time time) { match time { // The binding pattern below has a rest binding pattern to capture the additional // fields that may be specified in the open record value assigned to the `time` variable. // The condition here checks whether the open record has the field `meridiem`. var {hours, minutes, ...rest} if rest["meridiem"] !is () && rest["meridiem"] == "PM" => { io:println(hours + 12, ", ", minutes); } _ => { io:println(time.hours, ", ", time.minutes); } } } public function main() { Time time = {hours: 3, minutes: 20, "seconds": 40, "milli-seconds": 500}; matchTime1(time); matchTime2(time); Day day = {t: time}; matchTime3(day); Time time2 = {hours: 11, minutes: 21, "seconds": 52, "meridiem": "PM"}; matchTime4(time2); matchTime4(time); } ================================================ FILE: examples/mapping-binding-pattern-in-match-statement/mapping_binding_pattern_in_match_statement.md ================================================ # Mapping binding pattern in match statement Mapping binding patterns can be used in a match statement to bind parts of successfully matched mapping values to variables. The rest binding pattern (`...r`) can be used in the mapping binding pattern to bind mapping fields that are not explicitly bound in the binding pattern. This is particularly useful when working with open records. ::: code mapping_binding_pattern_in_match_statement.bal ::: ::: out mapping_binding_pattern_in_match_statement.out ::: ## Related links - [Rest Binding Pattern in Mapping Binding Pattern](/learn/by-example/rest-binding-pattern-in-mapping-binding-pattern/) - [Mapping binding pattern](/learn/by-example/mapping-binding-pattern/) - [Binding Patterns](/learn/by-example/binding-patterns/) - [Match Statement](/learn/by-example/match-statement/) ================================================ FILE: examples/mapping-binding-pattern-in-match-statement/mapping_binding_pattern_in_match_statement.metatags ================================================ description: This BBE introduces the use of mapping binding pattern in match statement, matching mapping values in match statement keywords: ballerina, ballerina by example, bbe, binding pattern, mapping binding pattern, match statement ================================================ FILE: examples/mapping-binding-pattern-in-match-statement/mapping_binding_pattern_in_match_statement.out ================================================ $ bal run mapping_binding_pattern_in_match_statement.bal 3, 20 3, 20, {"seconds":40,"milli-seconds":500} 3, 20 23, 21 3, 20 ================================================ FILE: examples/maps/maps.bal ================================================ import ballerina/io; public function main() { // Creates a `map` constrained by the `int` type. map ages = { "Tom": 23, "Jack": 34 }; // Gets the entry for `Tom`. int? v = ages["Tom"]; io:println(v); // As there exists an entry for `Tom`, it can be accessed using the `map:get()` method. // Using `ages["Tom"]` wouldn't work here because its type would be `int?` and not `int`. int age = ages.get("Tom"); io:println(age); // Adds a new entry for `Anne`. ages["Anne"] = 19; // The `map:hasKey()` method checks whether the map `age` has a member with `Jack` as the key. if ages.hasKey("Jack") { // The member with the key `Jack` can be removed using `map:remove()`. _ = ages.remove("Jack"); } // `map:keys()` returns the keys as an array of strings. foreach string name in ages.keys() { io:println(name, " : ", ages[name]); } } ================================================ FILE: examples/maps/maps.md ================================================ # Maps The `map` type is a mapping from strings to `T`. The map syntax is similar to JSON. Maps are mutable. `foreach` can be used to iterate over the values of the map `m`, and `m.keys()`, which returns the keys as an array of strings can be used to iterate over the keys. `m[k]` gets the entry for `k`; `nil` if missing. Use `m.get(k)` when you know that there is an entry for `k`. Two maps are equal (`==`) if they have the same set of keys and the values for each key are equal. ::: code maps.bal ::: ::: out maps.out ::: ## Related links - [Records](/learn/by-example/records/) - [Foreach statement](/learn/by-example/foreach-statement/) ================================================ FILE: examples/maps/maps.metatags ================================================ description: This BBE demonstrates creating a map type, storing key-value pairs, getting the keys from a map, and checking the equality of maps. keywords: ballerina, ballerina by example, bbe, maps, key, value, keys ================================================ FILE: examples/maps/maps.out ================================================ $ bal run maps.bal 23 23 Tom : 23 Anne : 19 ================================================ FILE: examples/match-guard-in-match-statement/match_guard_in_match_statement.bal ================================================ import ballerina/io; const switchStatus = "ON"; function matchValue(any val, boolean isObstructed, float powerPercentage) returns string { // The value of the `val` variable is matched against the given value match patterns. match val { // The `if !isObstructed` match guard is used. 1 if !isObstructed => { // This block will execute if `!isObstructed` is true. return "Move forward"; } // Use `|` to match more than one value. 2|3 => { return "Turn"; } //The `if 25.0 < powerPercentage` match guard is used. 4 if 25.0 < powerPercentage => { // This block will execute if `25.0 < powerPercentage` is true. return "Increase speed"; } "STOP" => { return "STOP"; } switchStatus => { return "Switch ON"; } // Use `_` to match type `any`. _ => { return "Invalid instruction"; } } } public function main() { io:println(matchValue(1, false, 36.0)); io:println(matchValue(4, false, 36.0)); } ================================================ FILE: examples/match-guard-in-match-statement/match_guard_in_match_statement.md ================================================ # Match guard in `match` statement A match-guard is an expression that is used in a `match` clause to determine whether the clause should be executed. A `match` clause will only be executed if its match-guard evaluates to true. A function call is only allowed with an expression in a match-guard when there is no possibility that it can mutate the value being matched. ::: code match_guard_in_match_statement.bal ::: ::: out match_guard_in_match_statement.out ::: ## Related links - [If statement](/learn/by-example/if-statement/) - [Match statement](/learn/by-example/match-statement/) ================================================ FILE: examples/match-guard-in-match-statement/match_guard_in_match_statement.metatags ================================================ description: This BBE demonstrates match guards in `match` statements of Ballerina. keywords: ballerina, ballerina by example, bbe, match, default, value match, match guard ================================================ FILE: examples/match-guard-in-match-statement/match_guard_in_match_statement.out ================================================ $ bal run match_guard_in_match_statement.bal Move forward Increase speed ================================================ FILE: examples/match-statement/match_statement.bal ================================================ import ballerina/io; const SWITCH_STATUS = "ON"; function matchValue(any val) returns string { // The value of the `val` variable is matched against the given value match patterns. match val { 1 => { return "Move forward"; } // Use `|` to match more than one value. 2|3 => { return "Turn"; } "STOP" => { return "STOP"; } SWITCH_STATUS => { return "Switch ON"; } // Use `_` to match type `any`. _ => { return "Invalid instruction"; } } } public function main() { io:println(matchValue(1)); io:println(matchValue(2)); io:println(matchValue("STOP")); io:println(matchValue(SWITCH_STATUS)); io:println(matchValue("default")); } ================================================ FILE: examples/match-statement/match_statement.md ================================================ # Match statement `match` statement is similar to `switch` statement in some other languages. It matches the value, not the type. `==` is used to test whether the left hand side matches the value being matched. The left hand side can be a simple literal (`nil`, `boolean`, `int`, `float`, `string`) identifier referring to a constant. Left hand side of `_` matches if the value is of type `any`. You can use `|` to match more than one value. ::: code match_statement.bal ::: ::: out match_statement.out ::: ## Related links - [If statement](/learn/by-example/if-statement/) ================================================ FILE: examples/match-statement/match_statement.metatags ================================================ description: This BBE demonstrates the `match` statement in Ballerina. keywords: ballerina, ballerina by example, bbe, match, default, value match ================================================ FILE: examples/match-statement/match_statement.out ================================================ $ bal run match_statement.bal Move forward Turn STOP Switch ON Invalid instruction ================================================ FILE: examples/match-statement-with-maps/match_statement_with_maps.bal ================================================ import ballerina/io; function execute(json j) { match j { // A `match` statement can be used to match maps. // Patterns on the left hand side in a match statement can have variable // parts that can be captured. {command: "print", amount: var x} => { if x is int { io:println("Int: ", x); } } _ => { io:println("invalid command"); } } } public function main() { execute({command: "print", amount: 100, status: "pending"}); execute({command: "print", amount: 10}); execute({command: "subtract", amount: 100}); } ================================================ FILE: examples/match-statement-with-maps/match_statement_with_maps.md ================================================ # Match statement with maps Match statement can be used to match maps. Patterns on the left hand side in a match statement can have variable parts that can be captured. Useful for working directly with `json`. Match semantics are open (may have fields other than those specified in the pattern). ::: code match_statement_with_maps.bal ::: ::: out match_statement_with_maps.out ::: ## Related links - [JSON type](/learn/by-example/json-type/) - [Match statement](/learn/by-example/match-statement/) ================================================ FILE: examples/match-statement-with-maps/match_statement_with_maps.metatags ================================================ description: This BBE demonstrates how to match json to pattern and check json has required structure in Ballerina. keywords: ballerina, ballerina by example, bbe, match, json, map ================================================ FILE: examples/match-statement-with-maps/match_statement_with_maps.out ================================================ $ bal run match_statement_with_maps.bal Int: 100 Int: 10 invalid command ================================================ FILE: examples/mcp-service/mcp_service.bal ================================================ import ballerina/log; import ballerina/mcp; import ballerina/random; import ballerina/time; type Weather record {| string location; decimal temperature; int humidity; int pressure; string condition; string timestamp; |}; type ForecastItem record {| string date; int high; int low; string condition; int precipitationChance; int windSpeed; |}; type WeatherForecast record {| string location; ForecastItem[] forecast; |}; // Define an MCP service attached to the MCP listener on port 9090. listener mcp:Listener mcpListener = new (9090); service mcp:Service /mcp on mcpListener { // The remote methods defined in this service become MCP tools. // The MCP listener handles listing and calling the tools on MCP requests. // The tool descriptions and schema are generated from the method signatures // and the documentation. # Get current weather for a city. # # + city - City name (e.g., "New York", "Tokyo") # + return - Current weather data for the specified city remote function getCurrentWeather(string city) returns Weather|error { Weather mockWeather = check getMockWeather(city); log:printInfo(string `Weather data retrieved for ${ city}: ${mockWeather.condition}, ${mockWeather.temperature}°C`); return mockWeather; }; # Get weather forecast for upcoming days. # # + location - City name or coordinates (e.g., "London", "40.7128,-74.0060") # + days - Number of days to forecast (1 - 7) # + return - Weather forecast for the specified location and days remote function getWeatherForecast(string location, int days) returns WeatherForecast|error { WeatherForecast mockForecast = { forecast: check getMockForecastItems(days), location }; log:printInfo(string `Forecast generated for ${location}: ${days} days with random data`); return mockForecast; } } function getMockWeather(string city) returns Weather|error => { condition: "Sunny", humidity: check random:createIntInRange(30, 70), location: city, pressure: check random:createIntInRange(1000, 1025), temperature: check random:createIntInRange(15, 30), timestamp: time:utcToString(time:utcNow()) }; function getMockForecastItems(int days) returns ForecastItem[]|error { string[] conditions = ["Sunny", "Cloudy", "Rainy", "Windy", "Stormy", "Snowy"]; return from int i in 1 ... days select { condition: conditions[check random:createIntInRange(0, conditions.length() - 1)], date: time:utcToString(time:utcAddSeconds(time:utcNow(), i * 86400)), high: check random:createIntInRange(20, 30), low: check random:createIntInRange(10, 20), precipitationChance: check random:createIntInRange(10, 50), windSpeed: check random:createIntInRange(5, 20) }; } ================================================ FILE: examples/mcp-service/mcp_service.md ================================================ # Model Context Protocol (MCP) service The Model Context Protocol (MCP) is a standard for connecting AI applications to external data sources, tools, and workflows (prompts). Ballerina's MCP library allows you to create MCP servers that expose tools. Remote methods of a `mcp:Service` service declaration automatically become MCP tools that AI assistants can discover and call. This example demonstrates how to create an MCP server that exposes weather-related tools, allowing AI assistants to discover and use tools to retrieve current weather data and forecasts for different locations. For more information on the underlying module, see the [`ballerina/mcp` module](https://lib.ballerina.io/ballerina/mcp/latest/). ::: code mcp_service.bal ::: ::: out mcp_service.out ::: ## Related links - [AI agents integrated with MCP servers example](/learn/by-example/ai-agent-mcp-integration/) ================================================ FILE: examples/mcp-service/mcp_service.metatags ================================================ description: This BBE demonstrates how to implement an MCP server. keywords: ballerina, ballerina by example, BBE, ai, llm, mcp ================================================ FILE: examples/mcp-service/mcp_service.out ================================================ $ bal run mcp_service.bal ================================================ FILE: examples/mcp-service-advanced/mcp_service_advanced.bal ================================================ import ballerina/log; import ballerina/mcp; import ballerina/random; import ballerina/time; type Weather record {| string location; decimal temperature; int humidity; int pressure; string condition; string timestamp; |}; type ForecastItem record {| string date; int high; int low; string condition; int precipitationChance; int windSpeed; |}; type WeatherForecast record {| string location; ForecastItem[] forecast; |}; // Define an MCP service attached to the MCP listener on port 9090. listener mcp:Listener mcpListener = new (9090); // Note how the service is declared with the `mcp:AdvancedService` type. service mcp:AdvancedService /mcp on mcpListener { isolated remote function onListTools() returns mcp:ListToolsResult|mcp:ServerError => { tools: [ { name: "getCurrentWeather", description: "Get current weather conditions for a location", inputSchema: { "type": "object", "properties": { "city": { "type": "string", "description": "City name or coordinates (e.g., 'London', '40.7128,-74.0060')" } }, "required": ["city"] } }, { name: "getWeatherForecast", description: "Get a 5-day weather forecast for a location", inputSchema: { "type": "object", "properties": { "city": { "type": "string", "description": "City name or coordinates (e.g., 'London', '40.7128,-74.0060')" }, "days": { "type": "integer", "description": "Number of days to forecast", "minimum": 1, "maximum": 7 } }, "required": ["city", "days"] } } ] }; isolated remote function onCallTool(mcp:CallToolParams params, mcp:Session? session) returns mcp:CallToolResult|mcp:ServerError { string name = params.name; do { if name == "getCurrentWeather" { // Attempt parsing the `arguments` field as a mapping consisting // with fields for each parameter type. record {| string city; |} arguments = check params.arguments.cloneWithType(); // Use the arguments in the function call. Weather weather = check getCurrentWeather(arguments.city); return {content: [{'type: "text", text: weather.toJsonString()}]}; } if name == "getWeatherForecast" { record {| string location; int days; |} {location, days} = check params.arguments.cloneWithType(); WeatherForecast forecast = check getWeatherForecast(location, days); return {content: [{'type: "text", text: forecast.toJsonString()}]}; } } on fail { return error("Invalid arguments"); } return error("Unknown tool: " + name); } } isolated function getCurrentWeather(string city) returns Weather|error { Weather mockWeather = check getMockWeather(city); log:printInfo(string `Weather data retrieved for ${ city}: ${mockWeather.condition}, ${mockWeather.temperature}°C`); return mockWeather; } isolated function getWeatherForecast(string location, int days) returns WeatherForecast|error { WeatherForecast mockForecast = { forecast: check getMockForecastItems(days), location }; log:printInfo(string `Forecast generated for ${location}: ${days} days with random data`); return mockForecast; } isolated function getMockWeather(string city) returns Weather|error => { condition: "Sunny", humidity: check random:createIntInRange(30, 70), location: city, pressure: check random:createIntInRange(1000, 1025), temperature: check random:createIntInRange(15, 30), timestamp: time:utcToString(time:utcNow()) }; isolated function getMockForecastItems(int days) returns ForecastItem[]|error { string[] conditions = ["Sunny", "Cloudy", "Rainy", "Windy", "Stormy", "Snowy"]; return from int i in 1 ... days select { condition: conditions[check random:createIntInRange(0, conditions.length() - 1)], date: time:utcToString(time:utcAddSeconds(time:utcNow(), i * 86400)), high: check random:createIntInRange(20, 30), low: check random:createIntInRange(10, 20), precipitationChance: check random:createIntInRange(10, 50), windSpeed: check random:createIntInRange(5, 20) }; } ================================================ FILE: examples/mcp-service-advanced/mcp_service_advanced.md ================================================ # Model Context Protocol (MCP) advanced service The Model Context Protocol (MCP) is a standard for connecting AI applications to external data sources, tools, and workflows (prompts). Ballerina's MCP library allows you to create MCP servers that expose tools. A `mcp:AdvancedService` service expects two remote methods, namely `onListTools` and `onCallTool`, to allow AI assistants to discover and call tools respectively. This example demonstrates how to create an MCP server that exposes weather-related tools via explicit MCP methods, allowing AI assistants to discover and use tools to retrieve current weather data and forecasts for different locations. For more information on the underlying module, see the [`ballerina/mcp` module](https://lib.ballerina.io/ballerina/mcp/latest/). ::: code mcp_service_advanced.bal ::: ::: out mcp_service_advanced.out ::: ## Related links - [The AI agents integrated with MCP servers example](/learn/by-example/ai-agent-mcp-integration/) ================================================ FILE: examples/mcp-service-advanced/mcp_service_advanced.metatags ================================================ description: This BBE demonstrates how to implement an MCP server, with explicit tool retrieval and usage methods. keywords: ballerina, ballerina by example, BBE, ai, llm, mcp ================================================ FILE: examples/mcp-service-advanced/mcp_service_advanced.out ================================================ $ bal run mcp_service_advanced.bal ================================================ FILE: examples/message-store-listener/message_store_listener.bal ================================================ import ballerina/messaging; import ballerina/log; // Create the main message store messaging:Store msgStore = new messaging:InMemoryMessageStore(); // Create the dead-letter message store messaging:Store dlStore = new messaging:InMemoryMessageStore(); // Create the message store listener listener messaging:StoreListener msgStoreListener = new (msgStore, { pollingInterval: 10, maxRetries: 2, retryInterval: 2, deadLetterStore: dlStore }); // Create the service to process messages service on msgStoreListener { isolated remote function onMessage(anydata payload) returns error? { log:printInfo("message received", payload = payload); // Simulate error scenario if payload == "error" { return error("Simulated processing error"); } } } public function main() returns error? { // Store a message check msgStore->store("Hello, World!"); // Store a message to simulate error scenario check msgStore->store("error"); } ================================================ FILE: examples/message-store-listener/message_store_listener.md ================================================ # Message Store Listener This example demonstrates how to use message store listeners for automated message processing. The `StoreListener` provides a polling-based mechanism to automatically retrieve and process messages from a message store, with built-in support for retries and dead letter handling. ::: code message_store_listener.bal ::: > **Tip:** Enable debug logs to see internal logs related to message flow ::: code Config.toml ::: ::: out message_store_listener.out ::: ## Related links - [`messaging` module - API documentation](https://lib.ballerina.io/ballerina/messaging/latest/) - [`messaging` module - Specification](https://ballerina.io/spec/messaging/) ================================================ FILE: examples/message-store-listener/message_store_listener.metatags ================================================ description: This BBE demonstrates how to implement a message store listener using the messaging library in Ballerina. keywords: ballerina, ballerina by example, bbe, messaging, store, in-memory, store listener, dead-letter store, dlq, poll ================================================ FILE: examples/message-store-listener/message_store_listener.out ================================================ $ bal run message_store_listener.bal time=2025-08-25T12:28:32.268+05:30 level=INFO module="" message="message received" payload="Hello, World!" time=2025-08-25T12:28:42.241+05:30 level=INFO module="" message="message received" payload="error" time=2025-08-25T12:28:42.247+05:30 level=ERROR module=ballerina/messaging message="error occurred while processing message" error={"causes":[],"message":"Simulated processing error","detail":{},"stackTrace":[{"callableName":"onMessage","moduleName":"$anonType$_0","fileName":"message_store_listener.bal","lineNumber":25},{"callableName":"execute","moduleName":"ballerina.messaging.1.PollAndProcessMessages","fileName":"msg_listener.bal","lineNumber":201}]} msgId="01f08180-e965-11c0-ad70-3bc9eb7490bd" time=2025-08-25T12:28:44.268+05:30 level=INFO module="" message="message received" payload="error" time=2025-08-25T12:28:44.272+05:30 level=ERROR module=ballerina/messaging message="error processing message on retry" error={"causes":[],"message":"Simulated processing error","detail":{},"stackTrace":[{"callableName":"onMessage","moduleName":"$anonType$_0","fileName":"message_store_listener.bal","lineNumber":25},{"callableName":"execute","moduleName":"ballerina.messaging.1.PollAndProcessMessages","fileName":"msg_listener.bal","lineNumber":211}]} retryAttempt=1 msgId="01f08180-e965-11c0-ad70-3bc9eb7490bd" time=2025-08-25T12:28:46.278+05:30 level=INFO module="" message="message received" payload="error" time=2025-08-25T12:28:46.281+05:30 level=ERROR module=ballerina/messaging message="error processing message on retry" error={"causes":[],"message":"Simulated processing error","detail":{},"stackTrace":[{"callableName":"onMessage","moduleName":"$anonType$_0","fileName":"message_store_listener.bal","lineNumber":25},{"callableName":"execute","moduleName":"ballerina.messaging.1.PollAndProcessMessages","fileName":"msg_listener.bal","lineNumber":211}]} retryAttempt=2 msgId="01f08180-e965-11c0-ad70-3bc9eb7490bd" time=2025-08-25T12:28:46.286+05:30 level=DEBUG module=ballerina/messaging message="message stored in dead letter store after max retries" msgId="01f08180-e965-11c0-ad70-3bc9eb7490bd" ================================================ FILE: examples/message-store-type/message_store_type.bal ================================================ import ballerina/log; import ballerina/messaging; import ballerina/uuid; // Custom message store implementation using a simple in-memory map isolated client class CustomMessageStore { *messaging:Store; private final map messages = {}; private final map pendingMessages = {}; isolated remote function store(anydata payload) returns error? { string id = uuid:createType1AsString(); lock { self.messages[id] = { id, payload: payload.cloneReadOnly() }; } log:printInfo("Message stored", id = id); } isolated remote function retrieve() returns messaging:Message|error? { lock { string[] keys = self.messages.keys(); if keys.length() > 0 { string id = keys[0]; readonly & messaging:Message message = self.messages.get(id); // Move message to pending state self.pendingMessages[id] = message; _ = self.messages.remove(id); return message; } return; } } isolated remote function acknowledge(string id, boolean success = true) returns error? { lock { if self.pendingMessages.hasKey(id) { if success { _ = self.pendingMessages.remove(id); log:printInfo("Message acknowledged", id = id); } else { // Move message back to available state for negative ack readonly & messaging:Message message = self.pendingMessages.get(id); self.messages[id] = message; _ = self.pendingMessages.remove(id); log:printInfo("Message negative acknowledged", id = id); } } } } } public function main() returns error? { // Create an instance of the custom message store messaging:Store customStore = new CustomMessageStore(); // Store and process a message check customStore->store("Hello, World!"); messaging:Message? msg = check customStore->retrieve(); if msg is messaging:Message { log:printInfo("Retrieved message", payload = msg.payload, id = msg.id); // Acknowledge the message check customStore->acknowledge(msg.id); } // Demonstrate negative acknowledgment check customStore->store("Test message"); msg = check customStore->retrieve(); if msg is messaging:Message { log:printInfo("Retrieved message for negative ack", payload = msg.payload, id = msg.id); check customStore->acknowledge(msg.id, false); // Retrieve the same message again after negative ack msg = check customStore->retrieve(); if msg is messaging:Message { log:printInfo("Message available again after negative ack", payload = msg.payload, id = msg.id); check customStore->acknowledge(msg.id); } } } ================================================ FILE: examples/message-store-type/message_store_type.md ================================================ # Message Store Type The `messaging` package in Ballerina provides messaging capabilities through the `Store` interface. This example demonstrates how to implement a custom message store by creating your own implementation of the `messaging:Store` type. The `messaging:Store` interface defines the contract for message storage, retrieval, and acknowledgment operations. By implementing this interface, you can create custom message stores that suit your specific requirements while maintaining compatibility with the messaging framework. ::: code message_store_type.bal ::: ::: out message_store_type.out ::: ## Related links - [`messaging` module - API documentation](https://lib.ballerina.io/ballerina/messaging/latest/) - [`messaging` module - Specification](https://ballerina.io/spec/messaging/) - [In-memory message store](/learn/by-example/in-memory-message-store/) ================================================ FILE: examples/message-store-type/message_store_type.metatags ================================================ description: This BBE demonstrates how to implement the messaging:Store type to create a custom message store in Ballerina. keywords: ballerina, ballerina by example, bbe, messaging, store, custom, implementation, type ================================================ FILE: examples/message-store-type/message_store_type.out ================================================ $ bal run message_store_type.bal time=2025-10-08T08:54:37.054+05:30 level=INFO module="" message="Message stored" id="01f0a3f6-5150-1888-861d-4d6c2cf6fef8" time=2025-10-08T08:54:37.065+05:30 level=INFO module="" message="Retrieved message" payload="Hello, World!" id="01f0a3f6-5150-1888-861d-4d6c2cf6fef8" time=2025-10-08T08:54:37.066+05:30 level=INFO module="" message="Message acknowledged" id="01f0a3f6-5150-1888-861d-4d6c2cf6fef8" time=2025-10-08T08:54:37.068+05:30 level=INFO module="" message="Message stored" id="01f0a3f6-5150-1888-a339-6e9cd9fc0e8f" time=2025-10-08T08:54:37.069+05:30 level=INFO module="" message="Retrieved message for negative ack" payload="Test message" id="01f0a3f6-5150-1888-a339-6e9cd9fc0e8f" time=2025-10-08T08:54:37.070+05:30 level=INFO module="" message="Message negative acknowledged" id="01f0a3f6-5150-1888-a339-6e9cd9fc0e8f" time=2025-10-08T08:54:37.071+05:30 level=INFO module="" message="Message available again after negative ack" payload="Test message" id="01f0a3f6-5150-1888-a339-6e9cd9fc0e8f" time=2025-10-08T08:54:37.071+05:30 level=INFO module="" message="Message acknowledged" id="01f0a3f6-5150-1888-a339-6e9cd9fc0e8f" ================================================ FILE: examples/meta.json ================================================ { "githubBallerinaByExampleBaseURL": "https://github.com/ballerina-platform/ballerina-distribution/tree/" } ================================================ FILE: examples/module-lifecycle/module_lifecycle.bal ================================================ import ballerina/io; // Usually, it is an error to import a module without using it but if you want to import a module because of what its initialization does, // then, use `as _` as done in this example. import ballerina/grpc as _; // A module can have an `init` function just like an object. // The initialization of a module ends by calling its `init` function if there is one. function init() { io:println("Hello world"); } ================================================ FILE: examples/module-lifecycle/module_lifecycle.md ================================================ # Module lifecycle All modules are initialized at program startup. Module initialization is ordered so that if module A imports module B, then module A is initialized after module B. The initialization phase ends by calling the `main` function if there is one. A module's listeners are registered during module initialization. If there are registered listeners, then the initialization phase is followed by the listening phase. The listening phase starts by calling the `start` method on each registered listener. The listening phase is terminated by signal (e.g. `SIGINT`, `SIGTERM`). ::: code module_lifecycle.bal ::: ::: out module_lifecycle.out ::: ================================================ FILE: examples/module-lifecycle/module_lifecycle.metatags ================================================ description: This BBE explains module lifecycle and its various phases. keywords: ballerina, ballerina by example, bbe, module initialization, listener ================================================ FILE: examples/module-lifecycle/module_lifecycle.out ================================================ $ bal run module_lifecycle.bal Hello world ================================================ FILE: examples/mqtt-client-basic-authentication/mqtt_client_basic_authentication.bal ================================================ import ballerina/http; import ballerina/mqtt; import ballerina/time; type TemperatureDetails readonly & record { string deviceId; time:Utc timestamp; decimal temperature; }; service / on new http:Listener(9090) { private final mqtt:Client temperaturePublisher; function init() returns error? { self.temperaturePublisher = check new ("tcp://localhost:1883", "temperature-pub-client", { connectionConfig: { username: "alice", password: "alice@123" } }); } resource function post temperature(TemperatureDetails temperatureDetails) returns http:Accepted|error { _ = check self.temperaturePublisher->publish("mqtt/topic", { payload: temperatureDetails.toJsonString().toBytes() }); return http:ACCEPTED; } } ================================================ FILE: examples/mqtt-client-basic-authentication/mqtt_client_basic_authentication.curl.out ================================================ $ curl http://localhost:9090/temperature -H "Content-type:application/json" -d "{\"deviceId\": \"device-id-1\", \"timestamp\": [1692679864,0.822397000], \"temperature\": 27.5}" ================================================ FILE: examples/mqtt-client-basic-authentication/mqtt_client_basic_authentication.md ================================================ # MQTT client - Basic authentication The `mqtt:Client` connects to an MQTT server via basic authentication and then, sends messages to the server. Basic authentication can be enabled by configuring the `username` and `password` in the `connectionConfig` of the `mqtt:ClientConfiguration`. This can be used to connect to an MQTT server secured with basic authentication. ::: code mqtt_client_basic_authentication.bal ::: ## Prerequisites - Start an [MQTT broker](https://mqtt.org/software/) instance, which is configured to use basic authentication. - Run the MQTT service given in the [MQTT service - Basic authentication](/learn/by-example/mqtt-service-basic-authentication) example. Run the program by executing the following command. ::: out mqtt_client_basic_authentication.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out mqtt_client_basic_authentication.curl.out ::: ## Related links - [`mqtt:ClientConfiguration` record - API documentation](https://lib.ballerina.io/ballerina/mqtt/latest#ClientConfiguration) - [MQTT client basic authentication - Specification](/spec/mqtt/#322-secure-client) ================================================ FILE: examples/mqtt-client-basic-authentication/mqtt_client_basic_authentication.metatags ================================================ description: How to configure an MQTT client to use basic authentication. keywords: ballerina, ballerina by example, bbe, mqtt, client, authentication, iot ================================================ FILE: examples/mqtt-client-basic-authentication/mqtt_client_basic_authentication.out ================================================ $ bal run mqtt_publisher_basic_auth.bal ================================================ FILE: examples/mqtt-client-publish-message/mqtt_client_publish_message.bal ================================================ import ballerina/http; import ballerina/mqtt; import ballerina/time; type TemperatureDetails readonly & record { string deviceId; time:Utc timestamp; decimal temperature; }; service / on new http:Listener(9090) { private final mqtt:Client temperaturePublisher; function init() returns error? { self.temperaturePublisher = check new (mqtt:DEFAULT_URL, "temperature-pub-client"); } resource function post temperature(TemperatureDetails temperatureDetails) returns http:Accepted|error { _ = check self.temperaturePublisher->publish("mqtt/topic", { payload: temperatureDetails.toJsonString().toBytes() }); return http:ACCEPTED; } } ================================================ FILE: examples/mqtt-client-publish-message/mqtt_client_publish_message.curl.out ================================================ $ curl http://localhost:9090/temperature -H "Content-type:application/json" -d "{\"deviceId\": \"device-id-1\", \"timestamp\": [1692679864,0.822397000], \"temperature\": 27.5}" ================================================ FILE: examples/mqtt-client-publish-message/mqtt_client_publish_message.md ================================================ # MQTT client - Publish message The `mqtt:Client` connects to a given MQTT server, and then, publishes messages to a specific topic in the server. An `mqtt:CLient` is created by giving the MQTT server URL and a unique ID. Once connected, the `publish` method is used to send messages to the MQTT server by providing the relevant topic and the message as the parameters. Use this to send messages to a topic in the MQTT server. ::: code mqtt_client_publish_message.bal ::: ## Prerequisites - Start an [MQTT broker](https://mqtt.org/software/) instance. - Run the MQTT service given in the [MQTT service - Subscribe to messages](/learn/by-example/mqtt-service-subscribe-message) example. Run the program by executing the following command. ::: out mqtt_client_publish_message.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out mqtt_client_publish_message.curl.out ::: ## Related links - [`mqtt:Client->publish` function - API documentation](https://lib.ballerina.io/ballerina/mqtt/latest#Client-publish) - [`mqtt:Client` functions - Specification](/spec/mqtt/#33-functions) ================================================ FILE: examples/mqtt-client-publish-message/mqtt_client_publish_message.metatags ================================================ description: How to send messages to a MQTT topic using a `mqtt:Client`. keywords: ballerina, ballerina by example, bbe, mqtt, publisher, iot ================================================ FILE: examples/mqtt-client-publish-message/mqtt_client_publish_message.out ================================================ $ bal run mqtt_publisher.bal ================================================ FILE: examples/mqtt-client-ssl/mqtt_client_ssl.bal ================================================ import ballerina/http; import ballerina/mqtt; import ballerina/time; type TemperatureDetails readonly & record { string deviceId; time:Utc timestamp; decimal temperature; }; service / on new http:Listener(9090) { private final mqtt:Client temperaturePublisher; function init() returns error? { self.temperaturePublisher = check new ("ssl://localhost:8883", "temperature-pub-client", { connectionConfig: { secureSocket: { cert: "./resources/path/to/public.crt" } } }); } resource function post temperature(TemperatureDetails temperatureDetails) returns http:Accepted|error { _ = check self.temperaturePublisher->publish("mqtt/topic", { payload: temperatureDetails.toJsonString().toBytes() }); return http:ACCEPTED; } } ================================================ FILE: examples/mqtt-client-ssl/mqtt_client_ssl.curl.out ================================================ $ curl http://localhost:9090/temperature -H "Content-type:application/json" -d "{\"deviceId\": \"device-id-1\", \"timestamp\": [1692679864,0.822397000], \"temperature\": 27.5}" ================================================ FILE: examples/mqtt-client-ssl/mqtt_client_ssl.md ================================================ # MQTT client - SSL/TLS The `mqtt:Client` connects to an MQTT server via SSL/TLS and then, sends messages to the server. SSL/TLS can be enabled by configuring the `secureSocket`, which requires a certificate or a truststore. Further, Mutual TLS (mTLS) can be enabled by providing a certificate and private key of the client or a keystore. Use this to connect to an MQTT server secured with SSL. >**Info:** For more information on the underlying module, see the [`mqtt` module](https://lib.ballerina.io/ballerina/mqtt/latest). ::: code mqtt_client_ssl.bal ::: ## Prerequisites - Start an [MQTT broker](https://mqtt.org/software/) instance, which is configured to use SSL/TLS. - Run the MQTT service given in the [MQTT service - SSL/TLS](/learn/by-example/mqtt-service-ssl) example. Run the program by executing the following command. ::: out mqtt_client_ssl.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out mqtt_client_ssl.curl.out ::: ## Related links - [`mqtt:SecureSocket` record - API documentation](https://lib.ballerina.io/ballerina/mqtt/latest#SecureSocket) - [MQTT secure client - Specification](/spec/mqtt/#322-secure-client) ================================================ FILE: examples/mqtt-client-ssl/mqtt_client_ssl.metatags ================================================ description: How to configure a MQTT client to use SSL encryption. keywords: ballerina, ballerina by example, bbe, mqtt, publisher, encryption, SSL ================================================ FILE: examples/mqtt-client-ssl/mqtt_client_ssl.out ================================================ $ bal run mqtt_client_ssl.bal ================================================ FILE: examples/mqtt-service-basic-authentication/mqtt_service_basic_authentication.bal ================================================ import ballerina/lang.value; import ballerina/log; import ballerina/mqtt; import ballerina/time; type TemperatureDetails readonly & record { string deviceId; time:Utc timestamp; decimal temperature; }; listener mqtt:Listener temperatureSubscriber = new ("tcp://localhost:1883", "temperature-sub-client", "mqtt/topic", { connectionConfig: { username: "alice", password: "alice@123" } }); service on temperatureSubscriber { remote function onMessage(mqtt:Message message) returns error? { TemperatureDetails details = check value:fromJsonStringWithType(check string:fromBytes(message.payload)); log:printInfo(string `Received temperature details from device: ${details.deviceId} at ${time:utcToString(details.timestamp)} with temperature: ${details.temperature}`); } } ================================================ FILE: examples/mqtt-service-basic-authentication/mqtt_service_basic_authentication.md ================================================ # MQTT service - Basic authentication The `mqtt:Service` receives messages from the MQTT server using the `mqtt:Listener` via basic authentication. Basic authentication can be enabled by configuring the `username` and `password` in the `connectionConfig` of the `mqtt:ListenerConfiguration`. Use this to connect to an MQTT server secured with basic authentication. ::: code mqtt_service_basic_authentication.bal ::: ## Prerequisites - Start a [MQTT broker](https://mqtt.org/software/) instance configured to use basic authentication. Run the program by executing the following command. ::: out mqtt_service_basic_authentication.out ::: >**Tip:** Run the MQTT client given in the [MQTT publisher - Basic authentication](/learn/by-example/mqtt-client-basic-authentication) example to produce some messages to the topic. ## Related links - [`mqtt:ListenerConfiguration` record - API documentation](https://lib.ballerina.io/ballerina/mqtt/latest#ListenerConfiguration) - [MQTT service basic authentication - Specification](/spec/mqtt/#422-secure-listener) ================================================ FILE: examples/mqtt-service-basic-authentication/mqtt_service_basic_authentication.metatags ================================================ description: How to configure a MQTT listener to use basic authentication. keywords: ballerina, ballerina by example, bbe, mqtt, listener, service, iot ================================================ FILE: examples/mqtt-service-basic-authentication/mqtt_service_basic_authentication.out ================================================ $ bal run mqtt_auth_service.bal time=2023-08-16T16:48:03.259+05:30 level=INFO module="" message="Received temperature details from device: device-id-1 at 2023-08-16T11:18:03.212400160Z with temperature: 27.5" ================================================ FILE: examples/mqtt-service-ssl/mqtt_service_ssl.bal ================================================ import ballerina/lang.value; import ballerina/log; import ballerina/mqtt; import ballerina/time; type TemperatureDetails readonly & record { string deviceId; time:Utc timestamp; decimal temperature; }; listener mqtt:Listener temperatureSubscriber = new ("ssl://localhost:8883", "temperature-sub-client", "mqtt/topic", { connectionConfig: { // Provide the relevant secure socket configurations by using `mqtt:SecureSocket`. secureSocket: { cert: "./resources/path/to/public.crt" } } }); service on temperatureSubscriber { remote function onMessage(mqtt:Message message) returns error? { TemperatureDetails details = check value:fromJsonStringWithType(check string:fromBytes(message.payload)); log:printInfo(string `Received temperature details from device: ${details.deviceId} at ${time:utcToString(details.timestamp)} with temperature: ${details.temperature}`); } } ================================================ FILE: examples/mqtt-service-ssl/mqtt_service_ssl.md ================================================ # MQTT service - SSL/TLS The `mqtt:Service` receives messages from the MQTT server using the `mqtt:Listener` via SSL/TLS. SSL/TLS can be enabled by configuring the `secureSocket`, which requires a certificate or a truststore. Further, Mutual TLS (mTLS) can be enabled by providing a certificate and private key of the service or a keystore. Use this to connect to an MQTT server secured with SSL. ::: code mqtt_service_ssl.bal ::: ## Prerequisites - Start an [MQTT broker](https://mqtt.org/software/) instance, which is configured to use SSL/TLS. Run the program by executing the following command. ::: out mqtt_service_ssl.out ::: >**Tip:** Run the MQTT client given in the [MQTT client - SSL/TLS](/learn/by-example/mqtt-client-ssl) example to publish some messages to the topic. ## Related links - [`mqtt:SecureSocket` record - API documentation](https://lib.ballerina.io/ballerina/mqtt/latest#SecureSocket) - [MQTT secure service - Specification](/spec/mqtt/#422-secure-listener) ================================================ FILE: examples/mqtt-service-ssl/mqtt_service_ssl.metatags ================================================ description: How to configure an MQTT listener to use SSL encryption. keywords: ballerina, ballerina by example, bbe, mqtt, listener, service, iot, SSL ================================================ FILE: examples/mqtt-service-ssl/mqtt_service_ssl.out ================================================ $ bal run mqtt_service_ssl.bal time=2023-08-16T16:48:03.259+05:30 level=INFO module="" message="Received temperature details from device: device-id-1 at 2023-08-16T11:18:03.212400160Z with temperature: 27.5" ================================================ FILE: examples/mqtt-service-subscribe-message/mqtt_service_subscribe_message.bal ================================================ import ballerina/lang.value; import ballerina/log; import ballerina/mqtt; import ballerina/time; type TemperatureDetails readonly & record { string deviceId; time:Utc timestamp; decimal temperature; }; service on new mqtt:Listener(mqtt:DEFAULT_URL, "temperature-sub-client", "mqtt/topic") { remote function onMessage(mqtt:Message message) returns error? { TemperatureDetails details = check value:fromJsonStringWithType(check string:fromBytes(message.payload)); log:printInfo(string `Received temperature details from device: ${details.deviceId} at ${time:utcToString(details.timestamp)} with temperature: ${details.temperature}`); } } ================================================ FILE: examples/mqtt-service-subscribe-message/mqtt_service_subscribe_message.md ================================================ # MQTT service - Subscribe to messages The `mqtt:Service` connects to a given MQTT server via the `mqtt:Listener`, and allows to receive messages from different topics. Once new messages are received from a subscribed topic, the `onMessage` method gets invoked. If there are errors when invoking the method, the `onError` remote method will be invoked with the relevant `error`. If the method is not implemented, the error will be logged to the console. This can be used to receive messages from a set of topics in an MQTT server. ::: code mqtt_service_subscribe_message.bal ::: ## Prerequisites - Start a [MQTT broker](https://mqtt.org/software/) instance. Run the program by executing the following command. ::: out mqtt_service_subscribe_message.out ::: >**Tip:** Run the MQTT client given in the [MQTT client - Publish message](/learn/by-example/mqtt-client-publish-message) example to publish some messages to the topic. ## Related links - [`mqtt:Listener` client object - API documentation](https://lib.ballerina.io/ballerina/mqtt/latest#Listener) - [MQTT service - Specification](/spec/mqtt/#43-usage) ================================================ FILE: examples/mqtt-service-subscribe-message/mqtt_service_subscribe_message.metatags ================================================ description: Create an MQTT service to subscribe to messages from an MQTT server using Ballerina. keywords: ballerina, ballerina by example, bbe, mqtt, subscriber, listener, service, iot ================================================ FILE: examples/mqtt-service-subscribe-message/mqtt_service_subscribe_message.out ================================================ $ bal run mqtt_service.bal time=2023-08-16T16:48:03.259+05:30 level=INFO module="" message="Received temperature details from device: device-id-1 at 2023-08-16T11:18:03.212400160Z with temperature: 27.5" ================================================ FILE: examples/multiple-key-fields/multiple_key_fields.bal ================================================ import ballerina/io; type Employee record { readonly string firstName; readonly string lastName; int salary; }; public function main() { // `employees` has a key sequence with the `firstName` and `lastName` fields. table key(firstName, lastName) employees = table [ {firstName: "John", lastName: "Smith", salary: 100}, {firstName: "John", lastName: "Bloggs", salary: 200} ]; // The key sequence provides keyed access to members of the `table`. Employee? e = employees["John", "Bloggs"]; io:println(e); // `employees` has a key sequence with the `firstName` and `lastName` fields. table key<[string, string]> employees2 = table key(firstName, lastName) [ {firstName: "John", lastName: "Smith", salary: 100}, {firstName: "John", lastName: "Bloggs", salary: 200} ]; e = employees2["John", "Smith"]; io:println(e); } ================================================ FILE: examples/multiple-key-fields/multiple_key_fields.md ================================================ # Multiple key fields A `table` provides access to its members using a key that comes from the `readonly` fields of the member. It is a key sequence, which is used to provide keyed access to its members. The key sequence is an ordered sequence of the field names. ::: code multiple_key_fields.bal ::: ::: out multiple_key_fields.out ::: ## Related links - [Table](/learn/by-example/table/) - [Structured keys](/learn/by-example/multiple-key-fields/) - [Maps](/learn/by-example/maps/) ================================================ FILE: examples/multiple-key-fields/multiple_key_fields.metatags ================================================ description: This BBE demonstrates how to create table with multiple keys, get entry from multiple key table. keywords: ballerina, ballerina by example, bbe, multiple key fields, keys, table, map ================================================ FILE: examples/multiple-key-fields/multiple_key_fields.out ================================================ $ bal run multiple_key_fields.bal {"firstName":"John","lastName":"Bloggs","salary":200} {"firstName":"John","lastName":"Smith","salary":100} ================================================ FILE: examples/multiple-receive/multiple_receive.bal ================================================ import ballerina/http; import ballerina/io; import ballerina/lang.runtime; type Response record { record { string 'worker; } args; }; type Result record { string|error a; string|error b; }; function fetch(string workerParam) returns string|error { http:Client cl = check new ("https://postman-echo.com"); Response response = check cl->/get('worker = workerParam); return response.args.'worker; } public function main() { // Workers `w1` and `w2` call the `fetch` function to retrieve content. The workers // send the result of calling the `fetch` function to the default worker. worker w1 { fetch("w1") -> function; } worker w2 { runtime:sleep(2); fetch("w2") -> function; } // The multiple receive action is used to receive values from both workers. Result result = <- {a: w1, b: w2}; io:println(result); } ================================================ FILE: examples/multiple-receive/multiple_receive.md ================================================ # Multiple receive The multiple receive action can be used to receive values corresponding to multiple send actions. It operates by waiting for the receipt of values from all the send actions, subsequently constructing a mapping value containing those values. ::: code multiple_receive.bal ::: ::: out multiple_receive.out ::: ================================================ FILE: examples/multiple-receive/multiple_receive.metatags ================================================ description: This BBE demonstrates the use of the multiple receive action in inter-worker communication keywords: ballerina, ballerina by example, bbe, worker, multiple receive ================================================ FILE: examples/multiple-receive/multiple_receive.out ================================================ $ bal run multiple_receive.bal {"a":"w1","b":"w2"} ================================================ FILE: examples/multiple-wait/multiple_wait.bal ================================================ import ballerina/http; import ballerina/io; type Result record { string|error a; string|error b; }; function multiFetch(string urlA, string urlB) returns Result { worker WA returns string|error { return fetch(urlA); } worker WB returns string|error { return fetch(urlB); } // The `wait` action can be used to wait for multiple named workers. return wait {a: WA, b: WB}; } public function main() returns error? { Result res = multiFetch("https://postman-echo.com/get?lang=ballerina", "https://postman-echo.com/get?greeting=hello"); io:println(res); return; } function fetch(string url) returns string|error { http:Client cl = check new (url); map payload = check cl->get(""); return payload["args"].toString(); } ================================================ FILE: examples/multiple-wait/multiple_wait.md ================================================ # Multiple wait The `wait` action can be used to wait for multiple named workers. Works with futures also. ::: code multiple_wait.bal ::: ::: out multiple_wait.out ::: ================================================ FILE: examples/multiple-wait/multiple_wait.metatags ================================================ description: This BBE demonstrates the multiple wait action keywords: ballerina, ballerina by example, bbe, multiple wait, worker ================================================ FILE: examples/multiple-wait/multiple_wait.out ================================================ $ bal run multiple_wait.bal {"a":"{"lang":"ballerina"}","b":"{"greeting":"hello"}"} ================================================ FILE: examples/mysql-atomic-transaction/mysql_atomic_transaction.bal ================================================ import ballerina/http; import ballerina/sql; import ballerinax/mysql; import ballerinax/mysql.driver as _; // The `Order` record to load records from `sales_order` table. type Order record {| string id; string orderDate; string productId; int quantity; |}; service / on new http:Listener(8080) { private final mysql:Client db; function init() returns error? { // Initiate the mysql client at the start of the service. This will be used // throughout the lifetime of the service. self.db = check new ("localhost", "root", "Test@123", "MUSIC_STORE", 3306); } resource function post 'order(Order salesOrder) returns http:Created|error { transaction { // Insert into the `sales_order` table. _ = check self.db->execute(`INSERT INTO MUSIC_STORE.sales_order VALUES (${salesOrder.id}, ${salesOrder.orderDate}, ${salesOrder.productId}, ${salesOrder.quantity});`); // Update product quantity as per the order. sql:ExecutionResult inventoryUpdate = check self.db->execute( `UPDATE inventory SET quantity = quantity - ${salesOrder.quantity} WHERE id = ${salesOrder.productId}`); // If the product is not found, rollback or commit transaction. if inventoryUpdate.affectedRowCount == 0 { rollback; return error(string `Product ${salesOrder.productId} not found.`); } else { check commit; return http:CREATED; } } on fail error e { // In case of error, the transaction block is rolled back automatically. if e is sql:DatabaseError { if e.detail().errorCode == 3819 { return error(string `Product ${salesOrder.productId} is out of stock.`); } } return e; } } } ================================================ FILE: examples/mysql-atomic-transaction/mysql_atomic_transaction.client.out ================================================ $ curl http://localhost:8080/order -d "{\"id\":\"S-123\", \"orderDate\":\"2022-12-08\", \"productId\":\"A-123\", \"quantity\":11}" -H "Content-Type: application/json" Product A-123 is out of stock. ================================================ FILE: examples/mysql-atomic-transaction/mysql_atomic_transaction.md ================================================ # Database Access - Atomic transactions The `mysql:Client` supports atomic units of work with multiple SQL statements. To achieve atomic database transactions use the Ballerina `transaction` package with the `mysql:Client`. The database makes all changes permanent when the transaction is committed or undoes all changes when the transaction is rolled back. > **Tip**: Checkout [`ballerinax/mssql`](https://central.ballerina.io/ballerinax/mssql), [`ballerinax/postgresql`](https://central.ballerina.io/ballerinax/postgresql), [`ballerinax/oracledb`](https://central.ballerina.io/ballerinax/oracledb), [`ballerinax/java.jdbc`](https://central.ballerina.io/ballerinax/java.jdbc) for other supported database clients. ::: code mysql_atomic_transaction.bal ::: ## Prerequisite - To set up the database, see the [Database Access Ballerina By Example - Prerequisites](https://github.com/ballerina-platform/ballerina-distribution/tree/master/examples/mysql-prerequisite). Run the service. ::: out mysql_atomic_transaction.server.out ::: Invoke the service by executing the following cURL command in a new terminal to post a new order. ::: out mysql_atomic_transaction.client.out ::: The syntax for using XA transactions (distributed transactions across multiple resources) is the same. Additionally, `useXADatasource` option should be enabled in the client, ::: code mysql_atomic_xa_transaction.bal ::: ## Related links - [`mysql:Client` - API documentation](https://lib.ballerina.io/ballerinax/mysql/latest/) - [`mysql:Client` - Specification](https://github.com/ballerina-platform/module-ballerinax-mysql/blob/master/docs/spec/spec.md#2-client) ================================================ FILE: examples/mysql-atomic-transaction/mysql_atomic_transaction.metatags ================================================ description: This BBE demonstrates how to use the MySQL client to execute a batch of DDL/DML operations inside a transaction block in Ballerina. keywords: ballerina, ballerina by example, bbe, mysql, insert, update, delete, batch update, transaction ================================================ FILE: examples/mysql-atomic-transaction/mysql_atomic_transaction.server.out ================================================ $ bal run mysql_atomic_transaction.bal ================================================ FILE: examples/mysql-atomic-transaction/mysql_atomic_xa_transaction.bal ================================================ mysql:Client mysqlClient = check new (user = "root", password = "Test@123", database = "CUSTOMER", options = {useXADatasource: true}); ================================================ FILE: examples/mysql-batch-execute-operation/mysql_batch_execute_operation.bal ================================================ import ballerina/http; import ballerina/sql; import ballerinax/mysql; import ballerinax/mysql.driver as _; // The `Album` record to load records from `albums` table. type Album record {| string id; string title; string artist; float price; |}; service / on new http:Listener(8080) { private final mysql:Client db; function init() returns error? { // Initiate the mysql client at the start of the service. This will be used // throughout the lifetime of the service. self.db = check new ("localhost", "root", "Test@123", "MUSIC_STORE", 3306); } resource function post albums(Album[] albums) returns http:Created|error { // Create a batch parameterized query. sql:ParameterizedQuery[] insertQueries = from Album album in albums select `INSERT INTO albums (id, title, artist, price) VALUES (${album.id}, ${album.title}, ${album.artist}, ${album.price})`; // Insert records in a batch. _ = check self.db->batchExecute(insertQueries); return http:CREATED; } } ================================================ FILE: examples/mysql-batch-execute-operation/mysql_batch_execute_operation.client.out ================================================ $ curl http://localhost:8080/albums -d "[{\"id\": \"B-121\", \"title\": \"Anti\", \"artist\":\"Rihanna\", \"price\": 23.99}]" -H "Content-Type: application/json" ================================================ FILE: examples/mysql-batch-execute-operation/mysql_batch_execute_operation.md ================================================ # Database Access - Batch execution The `mysql:Client` allows executing a batch of DDL/DML statements with the use of `batchExecute` method. This method requires `sql:ParameterizedQuery[]`-typed SQL statements as arguments. > **Tip**: Checkout [`ballerinax/mssql`](https://central.ballerina.io/ballerinax/mssql), [`ballerinax/postgresql`](https://central.ballerina.io/ballerinax/postgresql), [`ballerinax/oracledb`](https://central.ballerina.io/ballerinax/oracledb), [`ballerinax/java.jdbc`](https://central.ballerina.io/ballerinax/java.jdbc) for other supported database clients. ::: code mysql_batch_execute_operation.bal ::: ## Prerequisites - To set up the database, see the [Database Access Ballerina By Example - Prerequisites](https://github.com/ballerina-platform/ballerina-distribution/tree/master/examples/mysql-prerequisite). Run the service. ::: out mysql_batch_execute_operation.server.out ::: Invoke the service by executing the following cURL command in a new terminal to insert new record. ::: out mysql_batch_execute_operation.client.out ::: ## Related links - [`mysql:Client` - API documentation](https://lib.ballerina.io/ballerinax/mysql/latest/) - [`mysql:Client` - Specification](https://github.com/ballerina-platform/module-ballerinax-mysql/blob/master/docs/spec/spec.md#2-client) ================================================ FILE: examples/mysql-batch-execute-operation/mysql_batch_execute_operation.metatags ================================================ description: This BBE demonstrates how to use the MySQL client to execute a batch of DDL/DML operations in Ballerina. keywords: ballerina, ballerina by example, bbe, mysql, insert, update, delete, batch update ================================================ FILE: examples/mysql-batch-execute-operation/mysql_batch_execute_operation.server.out ================================================ $ bal run mysql_batch_execute_operation.bal ================================================ FILE: examples/mysql-call-stored-procedures/mysql_call_stored_procedures.bal ================================================ import ballerina/http; import ballerina/sql; import ballerinax/mysql; import ballerinax/mysql.driver as _; // The `Order` record to load records from `sales_order` table. type Order record {| string id; @sql:Column {name: "order_date"} string orderDate; @sql:Column {name: "product_id"} string productId; int quantity; |}; type Orders record {| int total; Order[] orders; |}; service / on new http:Listener(8080) { private final mysql:Client db; function init() returns error? { // Initiate the mysql client at the start of the service. This will be used // throughout the lifetime of the service. self.db = check new ("localhost", "root", "Test@123", "MUSIC_STORE", 3306); } resource function get orders/[string orderDate]() returns Orders|error { // Initializes the `INOUT` and `OUT` parameters for the procedure call. sql:DateValue filterDate = new (orderDate); sql:IntegerOutParameter total = new (); // Call the `get_sales_order` stored procedure. sql:ProcedureCallResult result = check self.db->call(`{CALL get_sales_order(${filterDate}, ${total})}`, [Order]); // Process procedure-call parameters. int totalCount = check total.get(); Order[] orders = []; // Process procedure-call query results. stream? resultStream = result.queryResult; if resultStream !is () { stream orderStream = >resultStream; orders = check from Order 'order in orderStream select 'order; } // Cleans up the resources. check result.close(); return { total: totalCount, orders: orders }; } } ================================================ FILE: examples/mysql-call-stored-procedures/mysql_call_stored_procedures.client.out ================================================ $ curl http://localhost:8080/orders/2022-12-09 {"total":2, "orders":[{"id":"S-123", "orderDate":"2022-12-09", "productId":"A-123", "quantity":2}, {"id":"S-321", "orderDate":"2022-12-09", "productId":"A-321", "quantity":1}]} ================================================ FILE: examples/mysql-call-stored-procedures/mysql_call_stored_procedures.md ================================================ # Database Access - Call stored procedures The `mysql:Client` allows executing a stored procedure with the use of `call` method. This method requires a `sql:ParameterizedQuery`-typed SQL CALL statement as the argument. > **Tip**: Checkout [`ballerinax/mssql`](https://central.ballerina.io/ballerinax/mssql), [`ballerinax/postgresql`](https://central.ballerina.io/ballerinax/postgresql), [`ballerinax/oracledb`](https://central.ballerina.io/ballerinax/oracledb), [`ballerinax/java.jdbc`](https://central.ballerina.io/ballerinax/java.jdbc) for other supported database clients. ::: code mysql_call_stored_procedures.bal ::: ## Prerequisites - To set up the database, see the [Database Access Ballerina By Example - Prerequisites](https://github.com/ballerina-platform/ballerina-distribution/tree/master/examples/mysql-prerequisite). Run the service. ::: out mysql_call_stored_procedures.server.out ::: Invoke the service by executing the following cURL command in a new terminal to post a new order. ::: out mysql_call_stored_procedures.client.out ::: If the procedure returns more than one result set, then those can be accessed by using, ```ballerina boolean isAvailable = getNextQueryResult(); ``` This will return whether next result set is available and update `queryResult` with the next result set. ## Related links - [`mysql:Client` - API documentation](https://lib.ballerina.io/ballerinax/mysql/latest/) - [`mysql:Client` - Specification](https://github.com/ballerina-platform/module-ballerinax-mysql/blob/master/docs/spec/spec.md#2-client) ================================================ FILE: examples/mysql-call-stored-procedures/mysql_call_stored_procedures.metatags ================================================ description: This BBE demonstrates how to use the MySQL client to execute a stored procedure in Ballerina. keywords: ballerina, ballerina by example, bbe, mysql, call, stored procedure, delete, procedure ================================================ FILE: examples/mysql-call-stored-procedures/mysql_call_stored_procedures.server.out ================================================ $ bal run mysql_call_stored_procedures.bal ================================================ FILE: examples/mysql-execute-operation/mysql_execute_operation.bal ================================================ import ballerina/http; import ballerinax/mysql; import ballerinax/mysql.driver as _; type Album record {| string id; string title; string artist; float price; |}; service / on new http:Listener(8080) { private final mysql:Client db; function init() returns error? { // Initiate the mysql client at the start of the service. This will be used // throughout the lifetime of the service. self.db = check new ("localhost", "root", "Test@123", "MUSIC_STORE", 3306); } resource function post album(Album album) returns Album|error { _ = check self.db->execute(` INSERT INTO Albums (id, title, artist, price) VALUES (${album.id}, ${album.title}, ${album.artist}, ${album.price});`); return album; } } ================================================ FILE: examples/mysql-execute-operation/mysql_execute_operation.client.out ================================================ curl http://localhost:8080/album -d "{\"id\": \"B-123\", \"title\": \"Loud\", \"artist\":\"Rihanna\", \"price\": 23.99}" -H "Content-Type: application/json" {"id":"B-123", "title":"Loud", "artist":"Rihanna", "price":23.99} ================================================ FILE: examples/mysql-execute-operation/mysql_execute_operation.md ================================================ # Database Access - DML and DDL operations The `mysql:Client` allows executing a DDL/DML statement with the use of `execute` method. This method requires a `sql:ParameterizedQuery`-typed SQL DDL/DML statement as the argument. > **Tip**: Checkout [`ballerinax/mssql`](https://central.ballerina.io/ballerinax/mssql), [`ballerinax/postgresql`](https://central.ballerina.io/ballerinax/postgresql), [`ballerinax/oracledb`](https://central.ballerina.io/ballerinax/oracledb), [`ballerinax/java.jdbc`](https://central.ballerina.io/ballerinax/java.jdbc) for other supported database clients. ::: code mysql_execute_operation.bal ::: ## Prerequisites - To set up the database, see the [Database Access Ballerina By Example - Prerequisites](https://github.com/ballerina-platform/ballerina-distribution/tree/master/examples/mysql-prerequisite). Run the service. ::: out mysql_execute_operation.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out mysql_execute_operation.client.out ::: ## Related links - [`mysql:Client` - API documentation](https://lib.ballerina.io/ballerinax/mysql/latest/) - [`mysql:Client` - Specification](https://github.com/ballerina-platform/module-ballerinax-mysql/blob/master/docs/spec/spec.md#2-client) ================================================ FILE: examples/mysql-execute-operation/mysql_execute_operation.metatags ================================================ description: This BBE demonstrates the use of a MySQL client with DDL and DML operations in Ballerina. keywords: ballerina, ballerina by example, bbe, mysql, insert, update, delete ================================================ FILE: examples/mysql-execute-operation/mysql_execute_operation.server.out ================================================ $ bal run mysql_execute_operation.bal ================================================ FILE: examples/mysql-prerequisite/README.md ================================================ # Database Access Ballerina By Example - Prerequisites `Database Access` BBEs are based on a Music Store use-case. To set up the MySQL database to run the BBEs, run the bal files by executing the command `bal run`. 1. setup_database.bal - This creates the `MUSIC_STORE` database and necessary tables. 2. create_stored_procedure.bal - This creates the `get_sales_order` procedure. ================================================ FILE: examples/mysql-prerequisite/create_stored_procedure.bal ================================================ import ballerina/sql; import ballerinax/mysql; import ballerinax/mysql.driver as _; // Initializes the database as a prerequisite to `Database Access - Call stored procedures` sample. public function main() returns sql:Error? { mysql:Client mysqlClient = check new ("localhost", "root", "Test@123", "MUSIC_STORE", 3306); // Creates the necessary stored procedures using the execute command. _ = check mysqlClient->execute(`CREATE PROCEDURE MUSIC_STORE.get_sales_order(INOUT date DATE, OUT totalCount INT) BEGIN SELECT count(id) INTO totalCount FROM sales_order WHERE order_date = date; SELECT * FROM sales_order WHERE order_date = date; END`); check mysqlClient.close(); } ================================================ FILE: examples/mysql-prerequisite/setup_database.bal ================================================ import ballerina/sql; import ballerinax/mysql; import ballerinax/mysql.driver as _; // Initializes the database as a prerequisite to `Database Access` 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 MUSIC_STORE;`); // Creates `albums` table in the database. _ = check mysqlClient->execute(`CREATE TABLE MUSIC_STORE.albums ( id VARCHAR(100) NOT NULL PRIMARY KEY, title VARCHAR(100), artist VARCHAR(100), price REAL );`); // Adds the records to the `albums` table. _ = check mysqlClient->execute(`INSERT INTO MUSIC_STORE.albums VALUES("A-123", "Lemonade", "Beyonce", 18.98);`); _ = check mysqlClient->execute(`INSERT INTO MUSIC_STORE.albums VALUES("A-321", "Renaissance", "Beyonce", 24.98);`); // Creates `artists` table in the database. _ = check mysqlClient->execute(`CREATE TABLE MUSIC_STORE.artists ( artist_id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY, first_name VARCHAR(300), last_name VARCHAR(300) );`); // Adds the records to the `artists` table. _ = check mysqlClient->execute(`INSERT INTO MUSIC_STORE.artists VALUES (1, "Beyonce", "Knowles");`); _ = check mysqlClient->execute(`INSERT INTO MUSIC_STORE.artists VALUES (2, "Rihanna", "Fenty");`); // Creates `inventory` in the database. _ = check mysqlClient->execute(`CREATE TABLE MUSIC_STORE.inventory ( id varchar(100) NOT NULL PRIMARY KEY, title varchar(100) DEFAULT NULL, artist varchar(100) DEFAULT NULL, price double DEFAULT NULL, quantity int NOT NULL, CHECK (quantity > 0) );`); // Adds the records to the `inventory` table. _ = check mysqlClient->execute(`INSERT INTO MUSIC_STORE.inventory VALUES ("A-123", "Lemonade", "Beyonce", 18.98, 10);`); _ = check mysqlClient->execute(`INSERT INTO MUSIC_STORE.inventory VALUES ("A-321", "Renaissance", "Beyonce", 24.98, 100);`); // Creates `sales_order` table in the database. _ = check mysqlClient->execute(`CREATE TABLE MUSIC_STORE.sales_order ( id varchar(100) NOT NULL PRIMARY KEY, order_date DATE NOT NULL, product_id varchar(100) NOT NULL, quantity int );`); // Adds the records to the `sales_order` table. _ = check mysqlClient->execute(`INSERT INTO MUSIC_STORE.sales_order VALUES ("S-123", "2022-12-09", "A-123", 2);`); _ = check mysqlClient->execute(`INSERT INTO MUSIC_STORE.sales_order VALUES ("S-321", "2022-12-09", "A-321", 1);`); _ = check mysqlClient->execute(`INSERT INTO MUSIC_STORE.sales_order VALUES ("S-456", "2022-12-10", "A-321", 3);`); check mysqlClient.close(); } ================================================ FILE: examples/mysql-query-column-mapping/mysql_query_column_mapping.bal ================================================ import ballerina/http; import ballerina/sql; import ballerinax/mysql; import ballerinax/mysql.driver as _; // The `Album` record to load records from `albums` table. type Artist record {| @sql:Column {name: "artist_id"} int artistId; @sql:Column {name: "last_name"} string lastName; @sql:Column {name: "first_name"} string firstName; |}; service / on new http:Listener(8080) { private final mysql:Client db; function init() returns error? { // Initiate the mysql client at the start of the service. This will be used // throughout the lifetime of the service. self.db = check new ("localhost", "root", "Test@123", "MUSIC_STORE", 3306); } resource function get artists() returns Artist[]|error { // Execute simple query to retrieve all records from the `artist` table. stream artistStream = self.db->query(`SELECT * FROM artists;`); // Process the stream and convert results to Artist[] or return error. return from Artist artist in artistStream select artist; } } ================================================ FILE: examples/mysql-query-column-mapping/mysql_query_column_mapping.client.out ================================================ $ curl http://localhost:8080/artists [{"artistId":1, "lastName":"Knowles", "firstName":"Beyonce"}, {"artistId":2, "lastName":"Fenty", "firstName":"Rihanna"}] ================================================ FILE: examples/mysql-query-column-mapping/mysql_query_column_mapping.md ================================================ # Database Access - Query with advanced mapping The `mysql:Client` allows querying the database with the use of `query` method. To map the table column name with a different Ballerina record field use the `sql:Column` annotation. > **Tip**: Checkout [`ballerinax/mssql`](https://central.ballerina.io/ballerinax/mssql), [`ballerinax/postgresql`](https://central.ballerina.io/ballerinax/postgresql), [`ballerinax/oracledb`](https://central.ballerina.io/ballerinax/oracledb), [`ballerinax/java.jdbc`](https://central.ballerina.io/ballerinax/java.jdbc) for other supported database clients. ::: code mysql_query_column_mapping.bal ::: ## Prerequisites - To set up the database, see the [Database Access Ballerina By Example - Prerequisites](https://github.com/ballerina-platform/ballerina-distribution/tree/master/examples/mysql-prerequisite). Run the service. ::: out mysql_query_column_mapping.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out mysql_query_column_mapping.client.out ::: ## Related links - [`mysql:Client` - API documentation](https://lib.ballerina.io/ballerinax/mysql/latest/) - [`mysql:Client` - Specification](https://github.com/ballerina-platform/module-ballerinax-mysql/blob/master/docs/spec/spec.md#2-client) ================================================ FILE: examples/mysql-query-column-mapping/mysql_query_column_mapping.metatags ================================================ description: This BBE demonstrates how to use the MySQL client for query operations with advanced mapping for column names. keywords: ballerina, ballerina by example, bbe, mysql ================================================ FILE: examples/mysql-query-column-mapping/mysql_query_column_mapping.server.out ================================================ $ bal run mysql_query_column_mapping.bal ================================================ FILE: examples/mysql-query-operation/mysql_query_operation.md ================================================ # Database Access - Simple query The `mysql:Client` allows querying the database with the use of `query` method. This method requires a `sql:ParameterizedQuery`-typed SQL statement as the argument. > **Tip**: Checkout [`ballerinax/mssql`](https://central.ballerina.io/ballerinax/mssql), [`ballerinax/postgresql`](https://central.ballerina.io/ballerinax/postgresql), [`ballerinax/oracledb`](https://central.ballerina.io/ballerinax/oracledb), [`ballerinax/java.jdbc`](https://central.ballerina.io/ballerinax/java.jdbc) for other supported database clients. ::: code mysql_simple_query.bal ::: ## Prerequisites - To set up the database, see the [Database Access Ballerina By Example - Prerequisites](https://github.com/ballerina-platform/ballerina-distribution/tree/master/examples/mysql-prerequisite). Run the service. ::: out mysql_simple_query.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out mysql_simple_query.client.out ::: ## Related links - [`mysql:Client` - API documentation](https://lib.ballerina.io/ballerinax/mysql/latest/) - [`mysql:Client` - Specification](https://github.com/ballerina-platform/module-ballerinax-mysql/blob/master/docs/spec/spec.md#2-client) ================================================ FILE: examples/mysql-query-operation/mysql_query_operation.metatags ================================================ description: This BBE demonstrates how to use the MySQL client select query operations with the stream return type in Ballerina. keywords: ballerina, ballerina by example, bbe, MySQL, query, select, data ================================================ FILE: examples/mysql-query-operation/mysql_simple_query.bal ================================================ import ballerina/http; import ballerina/sql; import ballerinax/mysql; import ballerinax/mysql.driver as _; // The `Album` record to load records from `albums` table. type Album record {| string id; string title; string artist; float price; |}; service / on new http:Listener(8080) { private final mysql:Client db; function init() returns error? { // Initiate the mysql client at the start of the service. This will be used // throughout the lifetime of the service. self.db = check new ("localhost", "root", "Test@123", "MUSIC_STORE", 3306); } resource function get albums() returns Album[]|error { // Execute simple query to retrieve all records from the `albums` table. stream albumStream = self.db->query(`SELECT * FROM albums`); // Process the stream and convert results to Album[] or return error. return from Album album in albumStream select album; } } ================================================ FILE: examples/mysql-query-operation/mysql_simple_query.client.out ================================================ $ curl http://localhost:8080/albums [{"id":"A-123", "title":"Lemonade", "artist":"Beyonce", "price":18.98}, {"id":"A-321", "title":"Renaissance", "artist":"Beyonce", "price":24.98}] ================================================ FILE: examples/mysql-query-operation/mysql_simple_query.server.out ================================================ $ bal run mysql_simple_query.bal ================================================ FILE: examples/mysql-query-row-operation/mysql_query_row.bal ================================================ import ballerina/http; import ballerina/sql; import ballerinax/mysql; import ballerinax/mysql.driver as _; // The `Album` record to load records from `albums` table. type Album record {| string id; string title; string artist; float price; |}; service / on new http:Listener(8080) { private final mysql:Client db; function init() returns error? { // Initiate the mysql client at the start of the service. This will be used // throughout the lifetime of the service. self.db = check new ("localhost", "root", "Test@123", "MUSIC_STORE", 3306); } resource function get albums/[string id]() returns Album|http:NotFound|error { // Execute simple query to fetch record with requested id. Album|sql:Error result = self.db->queryRow(`SELECT * FROM albums WHERE id = ${id}`); // Check if record is available or not if result is sql:NoRowsError { return http:NOT_FOUND; } else { return result; } } } ================================================ FILE: examples/mysql-query-row-operation/mysql_query_row.client.out ================================================ $ curl http://localhost:8080/albums/A-123 {"id":"A-123", "title":"Lemonade", "artist":"Beyonce", "price":18.98} ================================================ FILE: examples/mysql-query-row-operation/mysql_query_row.server.out ================================================ $ bal run mysql_query_row.bal ================================================ FILE: examples/mysql-query-row-operation/mysql_query_row_operation.md ================================================ # Database Access - Query with one result The `mysql:Client` allows querying the database for utmost one result with the use of `queryRow` method. This method requires a `sql:ParameterizedQuery`-typed SQL statement as the argument. > **Tip**: Checkout [`ballerinax/mssql`](https://central.ballerina.io/ballerinax/mssql), [`ballerinax/postgresql`](https://central.ballerina.io/ballerinax/postgresql), [`ballerinax/oracledb`](https://central.ballerina.io/ballerinax/oracledb), [`ballerinax/java.jdbc`](https://central.ballerina.io/ballerinax/java.jdbc) for other supported database clients. ::: code mysql_query_row.bal ::: ## Prerequisites - To set up the database, see the [Database Access Ballerina By Example - Prerequisites](https://github.com/ballerina-platform/ballerina-distribution/tree/master/examples/mysql-prerequisite). Run the service. ::: out mysql_query_row.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out mysql_query_row.client.out ::: ## Related links - [`mysql:Client` - API documentation](https://lib.ballerina.io/ballerinax/mysql/latest/) - [`mysql:Client` - Specification](https://github.com/ballerina-platform/module-ballerinax-mysql/blob/master/docs/spec/spec.md#2-client) ================================================ FILE: examples/mysql-query-row-operation/mysql_query_row_operation.metatags ================================================ description: This BBE demonstrates how to use the MySQL client select query row operations. keywords: ballerina, ballerina by example, bbe, MySQL, query, select, data ================================================ FILE: examples/named-worker-return-values/named_worker_return_values.bal ================================================ import ballerina/io; function demo(string s) returns int|error { // Named workers have a return type, which defaults to nil if not specified. worker A returns int|error { // A return statement in a named worker terminates the worker, not the function. // Similarly, when `check` is used and the expression evaluates to an `error`, // the `error` value is returned terminating only the worker. int x = check int:fromString(s); return x + 1; } io:println("In function worker"); // Waiting on a named worker will give its return value. int y = check wait A; return y + 1; } public function main() returns error? { int res = check demo("50"); io:println(res); res = check demo("50m"); io:println(res); } ================================================ FILE: examples/named-worker-return-values/named_worker_return_values.md ================================================ # Named worker return values Named workers have a return type, which defaults to nil. A `return` statement in a named worker terminates the worker, not the function. Similarly, when `check` is used and the expression evaluates to an `error`, the `error` value is returned terminating the worker. Waiting on a named worker will give its return value. ::: code named_worker_return_values.bal ::: ::: out named_worker_return_values.out ::: ================================================ FILE: examples/named-worker-return-values/named_worker_return_values.metatags ================================================ description: This BBE demonstrates named workers with return values keywords: ballerina, ballerina by example, bbe, worker, return ================================================ FILE: examples/named-worker-return-values/named_worker_return_values.out ================================================ $ bal run named_worker_return_values.bal In function worker 52 In function worker error: {ballerina/lang.int}NumberParsingError {"message":"'string' value '50m' cannot be converted to 'int'"} ================================================ FILE: examples/named-worker-with-on-fail-clause/named_worker_with_on_fail_clause.bal ================================================ import ballerina/io; public function main() { int[] values = [2, 3, 4, 5]; int value = 0; worker w1 { int index = check getIndex(values, value); index -> function; } on fail { // Handle the error thrown in the worker body. -1 -> function; } int|error:NoMessage result = <- w1 | w1; io:println(result); } function getIndex(int[] values, int value) returns int|error => let int? index = values.indexOf(value) in index ?: error("value not found"); ================================================ FILE: examples/named-worker-with-on-fail-clause/named_worker_with_on_fail_clause.md ================================================ # Named worker with on fail clause The `on fail` clause can be used with a named worker, to handle any errors that occur within the worker's body. ::: code named_worker_with_on_fail_clause.bal ::: ::: out named_worker_with_on_fail_clause.out ::: ================================================ FILE: examples/named-worker-with-on-fail-clause/named_worker_with_on_fail_clause.metatags ================================================ description: This BBE demonstrates the use of `on fail` clause with named workers keywords: ballerina, ballerina by example, bbe, worker, named workers, on fail ================================================ FILE: examples/named-worker-with-on-fail-clause/named_worker_with_on_fail_clause.out ================================================ $ bal run named_worker_with_on_fail_clause.bal -1 ================================================ FILE: examples/named-workers/named_workers.bal ================================================ import ballerina/io; public function main() { // Code before any named workers are executed before named workers start. io:println("Initializing"); final string greeting = "Hello"; // A function can declare named workers, which run concurrently with the function's default worker // and other named workers. worker A { // Variables declared before all named workers and function parameters are accessible by named workers. io:println(greeting + " from worker A"); } worker B { io:println(greeting + " from worker B"); } io:println(greeting + " from function worker"); } ================================================ FILE: examples/named-workers/named_workers.md ================================================ # Named workers Normally, all of a function's code belongs to the function's default worker, which has a single logical thread of control. A function can also declare named workers, which run concurrently with the function's default worker and other named workers. Code before any named workers is executed before named workers start. Variables declared before all named workers and function parameters are accessible in named workers. ::: code named_workers.bal ::: ::: out named_workers.out ::: ================================================ FILE: examples/named-workers/named_workers.metatags ================================================ description: This BBE demonstrates how workers can be used for concurrency keywords: ballerina, ballerina by example, bbe, worker, concurrency ================================================ FILE: examples/named-workers/named_workers.out ================================================ $ bal run named_workers.bal Initializing Hello from function worker Hello from worker B Hello from worker A ================================================ FILE: examples/named-workers-and-futures/named_workers_and_futures.bal ================================================ import ballerina/io; function demo() returns future { worker A returns int { return 42; } // A reference to a named worker can be implicitly converted into a `future`. return A; } type FuncInt function () returns int; function startInt(FuncInt f) returns future { // `start` is sugar for calling a function with a named worker and returning the named worker as a `future`. return start f(); } public function main() returns error? { future a = demo(); int b = check wait a; io:println(b); future c = startInt(() => 100); int d = check wait c; io:println(d); } ================================================ FILE: examples/named-workers-and-futures/named_workers_and_futures.md ================================================ # Named workers and futures Futures and workers are the same thing. A reference to a named worker can be implicitly converted into a `future`. `start` is sugar for calling a function with a named worker and returning the named worker as a `future`. Cancellation of futures only happens at yield points. ::: code named_workers_and_futures.bal ::: ::: out named_workers_and_futures.out ::: ================================================ FILE: examples/named-workers-and-futures/named_workers_and_futures.metatags ================================================ description: This BBE demonstrates futures keywords: ballerina, ballerina by example, bbe, future ================================================ FILE: examples/named-workers-and-futures/named_workers_and_futures.out ================================================ $ bal run named_workers_and_futures.bal 42 100 ================================================ FILE: examples/nats-basic-pub/nats_basic_pub.bal ================================================ import ballerina/http; import ballerinax/nats; type Order readonly & record { int orderId; string productName; decimal price; boolean isValid; }; service / on new http:Listener(9092) { private final nats:Client orderClient; function init() returns error? { // Initiate the NATS client at the start of the service. This will be used // throughout the lifetime of the service. self.orderClient = check new (nats:DEFAULT_URL); } resource function post orders(Order newOrder) returns http:Accepted|error { // Produces a message to the specified subject. check self.orderClient->publishMessage({ content: newOrder, subject: "orders.valid" }); return http:ACCEPTED; } } ================================================ FILE: examples/nats-basic-pub/nats_basic_pub.client.out ================================================ $ curl http://localhost:9092/orders -H "Content-type:application/json" -d "{\"orderId\": 1, \"productName\": \"Sport shoe\", \"price\": 27.5, \"isValid\": true}" ================================================ FILE: examples/nats-basic-pub/nats_basic_pub.md ================================================ # NATS client - Publish message The `nats:Client` allows publishing messages to a given subject. A `nats:Client` is created by passing the URL of the NATS broker. To publish messages, the `publishMessage` method is used, which requires the message and subject as arguments. Use it to publish messages that can be received by one or more subscribers. ::: code nats_basic_pub.bal ::: ## Prerequisites - Start an instance of the [NATS server](https://docs.nats.io/nats-concepts/what-is-nats/walkthrough_setup). - Run the NATS service given in the [NATS service - Consume message](/learn/by-example/nats-basic-sub/) example. Run the client program by executing the following command. ::: out nats_basic_pub.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out nats_basic_pub.client.out ::: ## Related links - [`nats:Client` client object - API documentation](https://lib.ballerina.io/ballerinax/nats/latest#Client) - [NATS publishing - Specification](https://github.com/ballerina-platform/module-ballerinax-nats/blob/master/docs/spec/spec.md#3-publishing) ================================================ FILE: examples/nats-basic-pub/nats_basic_pub.metatags ================================================ description: This example demonstrates producing a message to a subject in the NATS basic server. keywords: ballerina, ballerina by example, bbe, nats, basic, server, publish, subscribe ================================================ FILE: examples/nats-basic-pub/nats_basic_pub.server.out ================================================ $ bal run nats_basic_pub.bal ================================================ FILE: examples/nats-basic-reply/nats_basic_reply.bal ================================================ import ballerina/log; import ballerinax/nats; public type Order record { int orderId; string productName; decimal price; boolean isValid; }; // Binds the consumer to listen to the messages published to the 'orders.valid' subject. service "orders.valid" on new nats:Listener(nats:DEFAULT_URL) { remote function onRequest(Order 'order) returns string|error { if 'order.isValid { log:printInfo(string `Received valid order for ${'order.productName}`); return "Received valid order!"; } else { return "Received invalid order!"; } } } ================================================ FILE: examples/nats-basic-reply/nats_basic_reply.md ================================================ # NATS service - Send reply to request message The `nats:Service` allows listening to a given subject for incoming messages and sending responses. A `nats:Listener` is created by passing the URL of the NATS broker. A `nats:Service` attached to the listener can be used to send replies to incoming request messages using the `onRequest` remote method. The subject to listen to should be given as the service name or in the `subject` field of the `nats:ServiceConfig`. Use it to send reply messages to the request messages consumed by the subscriber. ::: code nats_basic_reply.bal ::: ## Prerequisites - Start an instance of the [NATS server](https://docs.nats.io/nats-concepts/what-is-nats/walkthrough_setup). Run the service by executing the following command. ::: out nats_basic_reply.out ::: >**Tip:** You can invoke the above service via the [NATS client](/learn/by-example/nats-basic-request/). ## Related links - [`nats` package - API documentation](https://lib.ballerina.io/ballerinax/nats/latest) - [NATS subscribing - Specification](https://github.com/ballerina-platform/module-ballerinax-nats/blob/master/docs/spec/spec.md#4-subscribing) ================================================ FILE: examples/nats-basic-reply/nats_basic_reply.metatags ================================================ description: This example demonstrates sending a reply message to a request received by a NATS subject. keywords: ballerina, ballerina by example, bbe, nats, basic, server, request, reply ================================================ FILE: examples/nats-basic-reply/nats_basic_reply.out ================================================ $ bal run nats-basic-reply.bal time = 2021-05-19T10:14:09.200+05:30 level = INFO module = "" message = "Received message: Hello from Ballerina" ================================================ FILE: examples/nats-basic-request/nats_basic_request.bal ================================================ import ballerina/http; import ballerina/log; import ballerinax/nats; type Order readonly & record { int orderId; string productName; decimal price; boolean isValid; }; public type StringMessage record {| *nats:AnydataMessage; string content; |}; service / on new http:Listener(9092) { private final nats:Client orderClient; function init() returns error? { // Initiate the NATS client at the start of the service. This will be used // throughout the lifetime of the service. self.orderClient = check new (nats:DEFAULT_URL); } resource function post orders(Order newOrder) returns http:Accepted|error { // Sends a request and returns the reply. StringMessage reply = check self.orderClient->requestMessage({ content: newOrder, subject: "orders.valid" }); log:printInfo("Reply message: " + reply.content); return http:ACCEPTED; } } ================================================ FILE: examples/nats-basic-request/nats_basic_request.client.out ================================================ $ curl http://localhost:9092/orders -H "Content-type:application/json" -d "{\"orderId\": 1, \"productName\": \"Sport shoe\", \"price\": 27.5, \"isValid\": true}" ================================================ FILE: examples/nats-basic-request/nats_basic_request.md ================================================ # NATS client - Send request message The `nats:Client` allows sending request messages to a given subject. A `nats:Client` is created by passing the URL of the NATS broker. The `requestMessage` method can be used to send requests to the NATS server by providing a target subject, an optional reply subject, the message content, and an optional duration for the timeout. After the request is sent, the application waits on the response with the given timeout. Use it to send request messages, which expect a reply back. ::: code nats_basic_request.bal ::: ## Prerequisites - Start an instance of the [NATS server](https://docs.nats.io/nats-concepts/what-is-nats/walkthrough_setup). - Run the NATS service given in the [NATS service - Send reply to request message](/learn/by-example/nats-basic-reply/) example. Run the client program by executing the following command. ::: out nats_basic_request.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out nats_basic_request.client.out ::: ## Related links - [`nats:Client` client object - API documentation](https://lib.ballerina.io/ballerinax/nats/latest#Client) - [NATS publishing - Specification](https://github.com/ballerina-platform/module-ballerinax-nats/blob/master/docs/spec/spec.md#3-publishing) ================================================ FILE: examples/nats-basic-request/nats_basic_request.metatags ================================================ description: This example demonstrates sending a request to a NATS subject in a basic NATS server. keywords: ballerina, ballerina by example, bbe, nats, basic, server, request, reply ================================================ FILE: examples/nats-basic-request/nats_basic_request.server.out ================================================ $ bal run nats_basic_request.bal time = 2022-12-13T20:48:14.375+05:30 level = INFO module = "" message = "Reply message: Received valid order!" ================================================ FILE: examples/nats-basic-sub/nats_basic_sub.bal ================================================ import ballerina/log; import ballerinax/nats; public type Order record { int orderId; string productName; decimal price; boolean isValid; }; // Binds the consumer to listen to the messages published to the 'orders.valid' subject. service "orders.valid" on new nats:Listener(nats:DEFAULT_URL) { remote function onMessage(Order 'order) returns error? { if 'order.isValid { log:printInfo(string `Received valid order for ${'order.productName}`); } } } ================================================ FILE: examples/nats-basic-sub/nats_basic_sub.md ================================================ # NATS service - Consume message The `nats:Service` listens to the given subject for incoming messages. When a publisher sends a message to a subject, any active service listening to that subject receives the message. A `nats:Listener` is created by passing the URL of the NATS broker. A `nats:Service` attached to the `nats:Listener` can be used to listen to a specific subject and consume incoming messages. The subject to listen to should be given as the service name or in the `subject` field of the `nats:ServiceConfig`. Use it to listen to messages sent to a particular subject. ::: code nats_basic_sub.bal ::: ## Prerequisites - Start an instance of the [NATS server](https://docs.nats.io/nats-concepts/what-is-nats/walkthrough_setup). Run the service by executing the following command. ::: out nats_basic_sub.out ::: >**Tip:** You can invoke the above service via the [NATS client](/learn/by-example/nats-basic-pub/). ## Related links - [`nats` package - API documentation](https://lib.ballerina.io/ballerinax/nats/latest) - [NATS subscribing - Specification](https://github.com/ballerina-platform/module-ballerinax-nats/blob/master/docs/spec/spec.md#4-subscribing) ================================================ FILE: examples/nats-basic-sub/nats_basic_sub.metatags ================================================ description: This example demonstrates consuming messages from a NATS subject in a basic NATS server. keywords: ballerina, ballerina by example, bbe, nats, basic, server, publish, subscribe ================================================ FILE: examples/nats-basic-sub/nats_basic_sub.out ================================================ $ bal run nats-basic-sub.bal time = 2021-05-19T10:15:49.269+05:30 level = INFO module = "" message = "Received message: Hello from Ballerina" ================================================ FILE: examples/nats-client-basic-auth/nats_client_basic_auth.bal ================================================ import ballerina/http; import ballerinax/nats; type Order readonly & record { int orderId; string productName; decimal price; boolean isValid; }; service / on new http:Listener(9092) { private final nats:Client orderClient; function init() returns error? { // Initiate the NATS client at the start of the service. This will be used // throughout the lifetime of the service. self.orderClient = check new (nats:DEFAULT_URL, // To secure the client connections using username/password authentication, provide the credentials // with the `nats:Credentials` record. auth = { username: "alice", password: "alice@123" } ); } resource function post orders(Order newOrder) returns http:Accepted|error { // Produces a message to the specified subject. check self.orderClient->publishMessage({ content: newOrder, subject: "orders.valid" }); return http:ACCEPTED; } } ================================================ FILE: examples/nats-client-basic-auth/nats_client_basic_auth.client.out ================================================ $ curl http://localhost:9092/orders -H "Content-type:application/json" -d "{\"orderId\": 1, \"productName\": \"Sport shoe\", \"price\": 27.5, \"isValid\": true}" ================================================ FILE: examples/nats-client-basic-auth/nats_client_basic_auth.md ================================================ # NATS client - Basic authentication The NATS authentication allows securing the client communication with the server. In this example, the underlying connection of the client is secured with Basic Authentication. A secured `nats:Client` can be created by passing the URL of the NATS broker and providing the authentication details using the `nats:Credentials` record. Use it to authenticate client connections using a username and password. ::: code nats_client_basic_auth.bal ::: ## Prerequisites - Start an instance of the [NATS server](https://docs.nats.io/nats-concepts/what-is-nats/walkthrough_setup). Run the client program by executing the following command. ::: out nats_client_basic_auth.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out nats_client_basic_auth.client.out ::: ## Related links - [`nats:Credentials` - API documentation](https://lib.ballerina.io/ballerinax/nats/latest#Credentials) - [NATS connection - Specification](https://github.com/ballerina-platform/module-ballerinax-nats/blob/master/docs/spec/spec.md#2-connection) ================================================ FILE: examples/nats-client-basic-auth/nats_client_basic_auth.metatags ================================================ description: This example demonstrates producing a message to a NATS subject using a connection with basic authentication. keywords: ballerina, ballerina by example, bbe, nats, basic, ssl, tls, authentication ================================================ FILE: examples/nats-client-basic-auth/nats_client_basic_auth.server.out ================================================ $ bal run nats_client_basic_auth.bal ================================================ FILE: examples/nats-client-secure-connection/nats_client_secure_connection.bal ================================================ import ballerina/http; import ballerinax/nats; type Order readonly & record { int orderId; string productName; decimal price; boolean isValid; }; service / on new http:Listener(9092) { private final nats:Client orderClient; function init() returns error? { // Initiate the NATS client at the start of the service. This will be used // throughout the lifetime of the service. self.orderClient = check new (nats:DEFAULT_URL, // To secure the client connection using TLS/SSL, the client needs to be configured with // a certificate file of the server. secureSocket = { cert: "../resource/path/to/public.crt" } ); } resource function post orders(Order newOrder) returns http:Accepted|error { // Produces a message to the specified subject. check self.orderClient->publishMessage({ content: newOrder, subject: "orders.valid" }); return http:ACCEPTED; } } ================================================ FILE: examples/nats-client-secure-connection/nats_client_secure_connection.client.out ================================================ $ curl http://localhost:9092/orders -H "Content-type:application/json" -d "{\"orderId\": 1, \"productName\": \"Sport shoe\", \"price\": 27.5, \"isValid\": true}" ================================================ FILE: examples/nats-client-secure-connection/nats_client_secure_connection.md ================================================ # NATS client - SSL/TLS The `nats:Client` can be configured to connect to the server via SSL/TLS by providing a certificate file. The certificate can be provided through the `secureSocket` field of the `nats:ConnectionConfiguration`. Use this to secure the communication between the client and the server. ::: code nats_client_secure_connection.bal ::: ## Prerequisites - Start an instance of the [NATS server](https://docs.nats.io/nats-concepts/what-is-nats/walkthrough_setup). Run the client program by executing the following command. ::: out nats_client_secure_connection.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out nats_client_secure_connection.client.out ::: ## Related links - [`nats:SecureSocket` - API documentation](https://lib.ballerina.io/ballerinax/nats/latest#SecureSocket) - [NATS connection- Specification](https://github.com/ballerina-platform/module-ballerinax-nats/blob/master/docs/spec/spec.md#2-connection) ================================================ FILE: examples/nats-client-secure-connection/nats_client_secure_connection.metatags ================================================ description: This example demonstrates producing a message to a NATS subject using a secured connection. keywords: ballerina, ballerina by example, bbe, nats, basic, ssl, tls, authentication ================================================ FILE: examples/nats-client-secure-connection/nats_client_secure_connection.server.out ================================================ $ bal run nats_client_secure_connection.bal ================================================ FILE: examples/nats-jetstream-pub/nats_jetstream_pub.bal ================================================ import ballerina/http; import ballerinax/nats; type Order readonly & record { int orderId; string productName; decimal price; boolean isValid; }; service / on new http:Listener(9092) { private final string SUBJECT_NAME = "orders"; private final nats:JetStreamClient orderClient; function init() returns error? { // Initiate a NATS client passing the URL of the NATS broker. nats:Client natsClient = check new (nats:DEFAULT_URL); // Initiate the NATS `JetStreamClient` at the start of the service. This will be used // throughout the lifetime of the service. self.orderClient = check new (natsClient); nats:StreamConfiguration config = { name: "demo", subjects: [self.SUBJECT_NAME], storageType: nats:MEMORY }; _ = check self.orderClient->addStream(config); } resource function post orders(Order newOrder) returns http:Accepted|error { // Produce a message to the specified subject. check self.orderClient->publishMessage({ subject: self.SUBJECT_NAME, content: newOrder.toString().toBytes() }); return http:ACCEPTED; } } ================================================ FILE: examples/nats-jetstream-pub/nats_jetstream_pub.client.out ================================================ $ curl http://localhost:9092/orders -H "Content-type:application/json" -d "{\"orderId\": 1, \"productName\": \"Sport shoe\", \"price\": 27.5, \"isValid\": true}" ================================================ FILE: examples/nats-jetstream-pub/nats_jetstream_pub.md ================================================ # NATS JetStream client - Publish message The `nats:JetStreamClient` allows you to publish messages to a specific subject. To create a `nats:JetStreamClient`, you need to provide a valid instance of the `nats:Client`. Before publishing messages, you should call the `addStream` function to configure the stream. This function requires a `nats:JetStreamConfiguration` object with the `name`, `subjects`, and `storageType` values. To publish messages, you can use the `publishMessage` method, which takes the message content and subject as arguments. This method allows you to send messages that can be received by one or more subscribers. ::: code nats_jetstream_pub.bal ::: ## Prerequisites - Start an instance of the [NATS JetStream server](https://docs.nats.io/running-a-nats-service/configuration/resource_management). - Run the NATS JetStream service given in the [NATS JetStream service - Consume message](/learn/by-example/nats-jetstream-sub/) example. Run the client program by executing the following command. ::: out nats_jetstream_pub.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out nats_jetstream_pub.client.out ::: ## Related links - [`nats:JetStreamClient` client object - API documentation](https://lib.ballerina.io/ballerinax/nats/latest/clients/JetStreamClient) ================================================ FILE: examples/nats-jetstream-pub/nats_jetstream_pub.metatags ================================================ description: This example demonstrates producing a message to a subject in a NATS JetStream server. keywords: ballerina, ballerina by example, bbe, nats, jetstream, server, publish, subscribe ================================================ FILE: examples/nats-jetstream-pub/nats_jetstream_pub.server.out ================================================ $ bal run nats_jetstream_pub.bal ================================================ FILE: examples/nats-jetstream-sub/nats_jestream_sub.bal ================================================ import ballerina/log; import ballerinax/nats; type Order readonly & record { int orderId; string productName; decimal price; boolean isValid; }; // Initiate a NATS client passing the URL of the NATS broker. nats:Client natsClient = check new (nats:DEFAULT_URL); // Initialize a NATS JetStream listener. listener nats:JetStreamListener subscription = new (natsClient); const string SUBJECT_NAME = "orders"; @nats:StreamServiceConfig { subject: SUBJECT_NAME, autoAck: false } // Bind the consumer to listen to the messages published to the 'orders' subject. service nats:JetStreamService on subscription { remote function onMessage(nats:JetStreamMessage message) returns error? { string stringContent = check string:fromBytes(message.content); json jsonContent = check stringContent.fromJsonString(); Order 'order = check jsonContent.cloneWithType(); if 'order.isValid { log:printInfo(string `Received valid order for ${'order.productName}`); } } } ================================================ FILE: examples/nats-jetstream-sub/nats_jestream_sub.md ================================================ # NATS JetStream service - Consume message The `nats:JetStreamService` listens to a specified subject for incoming messages. Whenever a publisher sends a message to that subject, any active service listening to it will receive the message. You need to provide an instance of the `nats:Client` to create a `nats:JetStreamListener`. Once you have a `nats:JetStreamListener`, you can attach a `nats:JetStreamService` to it in order to listen to a specific subject and consume incoming messages. The subject to listen to can be either specified as the service path or provided in the `subject` field of the `nats:StreamServiceConfig`. This setup allows you to effectively listen to messages sent to a particular subject. ::: code nats_jestream_sub.bal ::: ## Prerequisites - Start an instance of the [NATS JetStream server](https://docs.nats.io/running-a-nats-service/configuration/resource_management). Run the service by executing the following command. ::: out nats_jestream_sub.out ::: >**Tip:** You can invoke the above service via the [NATS JetStream client](/learn/by-example/nats-jetstream-pub/). ## Related links - [`nats` package - API documentation](https://lib.ballerina.io/ballerinax/nats/latest) ================================================ FILE: examples/nats-jetstream-sub/nats_jestream_sub.metatags ================================================ description: This example demonstrates consuming messages from a subject in a NATS JetStream server. keywords: ballerina, ballerina by example, bbe, nats, jetstream, server, publish, subscribe ================================================ FILE: examples/nats-jetstream-sub/nats_jestream_sub.out ================================================ $ bal run nats_jetstream_sub.bal time = 2023-06-20T20:17:28.026+05:30 level = INFO module = "" message = "Received valid order for Sport shoe" ================================================ FILE: examples/nats-service-basic-auth/nats_service_basic_auth.bal ================================================ import ballerina/log; import ballerinax/nats; public type Order record { int orderId; string productName; decimal price; boolean isValid; }; // Initializes a NATS listener with TLS/SSL and username/password authentication. listener nats:Listener orderListener = new (nats:DEFAULT_URL, // To secure the client connections using username/password authentication, provide the credentials // with the `nats:Credentials` record. auth = { username: "alice", password: "alice@123" } ); // Binds the consumer to listen to the messages published to the 'orders.valid' subject. service "orders.valid" on orderListener { remote function onMessage(Order 'order) returns error? { if 'order.isValid { log:printInfo(string `Received valid order for ${'order.productName}`); } } } ================================================ FILE: examples/nats-service-basic-auth/nats_service_basic_auth.md ================================================ # NATS service - Basic authentication The NATS authentication allows securing the client communication with the server. In this example, the underlying connection of the listener is secured with Basic Authentication. A secured `nats:Listener` can be created by passing the URL of the NATS broker and providing the authentication details using the `nats:Credentials` record. Use it to authenticate client connections using a username and password. ::: code nats_service_basic_auth.bal ::: ## Prerequisites - Start an instance of the [NATS server](https://docs.nats.io/nats-concepts/what-is-nats/walkthrough_setup). Run the service by executing the following command. ::: out nats_service_basic_auth.out ::: ## Related links - [`nats:Credentials` - API documentation](https://lib.ballerina.io/ballerinax/nats/latest#Credentials) - [NATS connection - Specification](https://github.com/ballerina-platform/module-ballerinax-nats/blob/master/docs/spec/spec.md#2-connection) ================================================ FILE: examples/nats-service-basic-auth/nats_service_basic_auth.metatags ================================================ description: This example demonstrates consuming messages from a NATS subject using a connection with basic authentication. keywords: ballerina, ballerina by example, bbe, nats, basic, ssl, tls, authentication ================================================ FILE: examples/nats-service-basic-auth/nats_service_basic_auth.out ================================================ $ bal run nats-service-basic-auth.bal time = 2021-05-19T10:15:49.269+05:30 level = INFO module = "" message = "Received message: Hello from Ballerina" ================================================ FILE: examples/nats-service-constraint-validation/nats_service_constraint_validation.bal ================================================ import ballerina/constraint; import ballerina/log; import ballerinax/nats; public type Order record { int orderId; // Add a constraint to allow only string values of length between 1 and 30. @constraint:String {maxLength: 30, minLength: 1} string productName; decimal price; boolean isValid; }; // Binds the consumer to listen to the messages published to the 'orders.valid' subject. service "orders.valid" on new nats:Listener(nats:DEFAULT_URL) { remote function onMessage(Order 'order) returns error? { if 'order.isValid { log:printInfo(string `Received valid order for ${'order.productName}`); } } // When an error occurs, `onError` gets invoked. remote function onError(nats:AnydataMessage message, nats:Error err) { if err is nats:PayloadValidationError { log:printError("Payload validation failed", err); } } } ================================================ FILE: examples/nats-service-constraint-validation/nats_service_constraint_validation.md ================================================ # NATS service - Constraint validation The Ballerina constraint module allows you to add additional constraints to the message content. The constraints can be added to a given data type using different annotations. When a message with a constraint is received from the NATS server, it is validated internally. This validation happens soon after the successful data-binding of the message content before executing the `onMessage` remote method. If the validation fails, the `onError` remote method is invoked with the error type `nats:PayloadValidationError`. Use this to validate the message content as the application receives it, which allows you to guard against unnecessary remote method processing and malicious content. ::: code nats_service_constraint_validation.bal ::: ## Prerequisites - Start an instance of the [NATS server](https://docs.nats.io/nats-concepts/what-is-nats/walkthrough_setup). Run the service by executing the following command. ::: out nats_service_constraint_validation.out ::: >**Tip:** You can invoke the above service via the [NATS client](/learn/by-example/nats-basic-pub/) example with a valid product name (0 < length <= 30), then with an invalid product name and again with a valid product name. ## Related links - [`nats:PayloadValidationError` error type - API documentation](https://lib.ballerina.io/ballerinax/nats/latest#PayloadValidationError) - [`nats` package - Specification](https://github.com/ballerina-platform/module-ballerinax-nats/blob/master/docs/spec/spec.md) - [`constraint` package - API documentation](https://lib.ballerina.io/ballerina/constraint/latest) ================================================ FILE: examples/nats-service-constraint-validation/nats_service_constraint_validation.metatags ================================================ description: This example demonstrates validating a payload according to the constraints defined in the payload record. keywords: ballerina, ballerina by example, bbe, nats, consumer, listener, service, constraint, validation ================================================ FILE: examples/nats-service-constraint-validation/nats_service_constraint_validation.out ================================================ $ bal run nats_service_constraint_validation.bal time = 2022-12-05T08:44:43.093+05:30 level = INFO module = "" message = "Received valid order for Sport shoe" time = 2022-12-05T08:45:03.136+05:30 level = ERROR module = "" message = "Payload validation failed" error = "Validation failed for \'$.productName:maxLength\' constraint(s)." time = 2022-12-05T08:45:19.702+05:30 level = INFO module = "" message = "Received valid order for Sport shoe" ================================================ FILE: examples/nats-service-secure-connection/nats_service_secure_connection.bal ================================================ import ballerina/log; import ballerinax/nats; public type Order record { int orderId; string productName; decimal price; boolean isValid; }; // Initializes a NATS listener with TLS/SSL and username/password authentication. listener nats:Listener orderListener = new (nats:DEFAULT_URL, // To secure the client connection using TLS/SSL, the client needs to be configured with // a certificate file of the server. secureSocket = { cert: "../resource/path/to/public.crt" } ); // Binds the consumer to listen to the messages published to the 'orders.valid' subject. service "orders.valid" on orderListener { remote function onMessage(Order 'order) returns error? { if 'order.isValid { log:printInfo(string `Received valid order for ${'order.productName}`); } } } ================================================ FILE: examples/nats-service-secure-connection/nats_service_secure_connection.md ================================================ # NATS service - SSL/TLS The `nats:Listener` can be configured to connect to the server via SSL/TLS by providing a certificate file. The certificate can be provided through the `secureSocket` field of the `nats:ConnectionConfiguration`. Use this to secure the communication between the client and the server. ::: code nats_service_secure_connection.bal ::: ## Prerequisites - Start an instance of the [NATS server](https://docs.nats.io/nats-concepts/what-is-nats/walkthrough_setup). Run the service by executing the following command. ::: out nats_service_secure_connection.out ::: ## Related links - [`nats:SecureSocket` - API documentation](https://lib.ballerina.io/ballerinax/nats/latest#SecureSocket) - [NATS connection - Specification](https://github.com/ballerina-platform/module-ballerinax-nats/blob/master/docs/spec/spec.md#2-connection) ================================================ FILE: examples/nats-service-secure-connection/nats_service_secure_connection.metatags ================================================ description: This example demonstrates consuming messages from a NATS subject using a secured connection. keywords: ballerina, ballerina by example, bbe, nats, basic, ssl, tls, authentication ================================================ FILE: examples/nats-service-secure-connection/nats_service_secure_connection.out ================================================ $ bal run nats-service-secure-connection.bal time = 2021-05-19T10:15:49.269+05:30 level = INFO module = "" message = "Received message: Hello from Ballerina" ================================================ FILE: examples/natural-expressions/natural_expressions.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(); # Represents a tourist attraction. type Attraction record {| # The name of the attraction string name; # The city where the attraction is located string city; # A notable feature or highlight of the attraction string highlight; |}; function getAttractions(int count, string country, string interest) returns Attraction[]|error { // Use a natural expression to get attractions. // The JSON schema generated for the expected type (`Attraction[]`) is // used in the request to the large language model, and the // result is automatically parsed into an array of `Attraction` records. Attraction[]|error attractions = // Specify the model provider in the natural expression. // Use insertions to insert expressions (e.g., parameters) into the prompt. natural (model) { Give me the top ${count} tourist attractions in ${country} for visitors interested in ${interest}. For each attraction, the highlight should be one sentence describing what makes it special or noteworthy. }; return attractions; } public function main() returns error? { Attraction[] attractions = check getAttractions(3, "Sri Lanka", "Wildlife"); foreach Attraction attraction in attractions { io:println("Name: ", attraction.name); io:println("City: ", attraction.city); io:println("Highlight: ", attraction.highlight, "\n"); } } ================================================ FILE: examples/natural-expressions/natural_expressions.md ================================================ # Natural expressions Natural expressions provide a language-level abstraction for integrating with large language models (LLMs), enabling developers to seamlessly combine natural language instructions with Ballerina code while maintaining programming language principles and leveraging the strengths of the type system. Unlike simple method calls, natural expressions clearly distinguish between traditional logic and LLM-driven logic. Natural expressions use the `ai:ModelProvider` type as a unified interface for working with different LLM providers such as [ballerinax/ai.openai](https://central.ballerina.io/ballerinax/ai.openai/latest), [ballerinax/ai.anthropic](https://central.ballerina.io/ballerinax/ai.anthropic/latest), and others. The expression automatically generates a JSON schema from the expected return type, sends the prompt to the LLM, and binds the response to the specified type. > 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: This feature is supported on Swan Lake Update 13 - Milestone 3 (2201.13.0-m3) or newer versions. This is currently an experimental feature and requires the `--experimental` flag to be used with `bal` commands. For more information on the underlying module, see the [`ballerina/ai` module](https://lib.ballerina.io/ballerina/ai/latest/). ::: code natural_expressions.bal ::: ::: out natural_expressions.out ::: ## Related links - [Natural Language is Code: A hybrid approach with Natural Programming](https://blog.ballerina.io/posts/2025-04-26-introducing-natural-programming/) - [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/natural-expressions/natural_expressions.metatags ================================================ description: This BBE demonstrates how to do direct LLM calls using natural expressions. keywords: ballerina, ballerina by example, BBE, ai, llm, model-provider, natural expressions ================================================ FILE: examples/natural-expressions/natural_expressions.out ================================================ $ bal run --experimental natural_expressions.bal Name: Yala National Park City: Yala Highlight: Home to the highest density of leopards in the world, Yala offers thrilling wildlife safaris. Name: Wilpattu National Park City: Wilpattu Highlight: Famous for its natural lakes and diverse wildlife, including elephants and sloth bears. Name: Udawalawe National Park City: Udawalawe Highlight: Known for its large herds of elephants, Udawalawe is a sanctuary for these magnificent creatures. ================================================ FILE: examples/nested-arrays/nested_arrays.bal ================================================ import ballerina/io; public function main() { // Declare an array of length 3, where the members are arrays of length 2. string[3][2] orderItems = [["carrot", "apple"], ["avocado", "egg"], ["fish", "banana"]]; io:println(orderItems); // Declare an array of length 2, where the element type is an array of variable length. string[2][] orderItems2 = [["carrot", "apple"], ["avocado", "egg", "fish", "banana"]]; io:println(orderItems2); // Inferring supported only for the first dimension of the array type descriptor string[*][] orderItems3 = [["carrot", "apple"], ["avocado", "egg", "fish", "banana"]]; io:println(orderItems3); // Accessing a nested array. // This will access first item of the third order. string item = orderItems3[1][0]; io:println(item); // Updating a nested array. // This will update first item of the third order. orderItems3[1][0] = "apple"; io:println(orderItems3); } ================================================ FILE: examples/nested-arrays/nested_arrays.md ================================================ # Nested arrays Ballerina supports nested arrays where the element type is also an array type `T[p][q][r]`. Specifically, `T[p][q][r]` is interpreted as `((T[r])[q])[p]`. Hence, `T[p][q]` will construct an array of size `p` where the element type is `T[q]`. This is to be aligned with the member access expression where `v[i][j]` will evaluate to a value of type `T` if and only if `0 ≤ i ≤ p` and `0 ≤ j ≤ q`. ::: code nested_arrays.bal ::: ::: out nested_arrays.out ::: ## Related links - [Arrays](/learn/by-example/arrays) - [Manipulating an array `(lang.array)`](https://lib.ballerina.io/ballerina/lang.array) - [Tuples](/learn/by-example/tuples) - [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/nested-arrays/nested_arrays.metatags ================================================ description: BBE on creating nested array, create multidimensional array, create array of arrays, Accessing a nested array, Updating a nested array. keywords: ballerina, ballerina by example, bbe, collection, array, multidimensional, nested, array of arrays, list, access, update, matrix ================================================ FILE: examples/nested-arrays/nested_arrays.out ================================================ $ bal run nested_arrays.bal [["carrot","apple"],["avocado","egg"],["fish","banana"]] [["carrot","apple"],["avocado","egg","fish","banana"]] [["carrot","apple"],["avocado","egg","fish","banana"]] avocado [["carrot","apple"],["apple","egg","fish","banana"]] ================================================ FILE: examples/nested-query-expressions/nested_query_expressions.bal ================================================ import ballerina/io; type Person record {| string name; int age; |}; public function main() { Person[] teamA = [{name: "Alex", age: 23}, {name: "John", age: 24}]; Person[] teamB = [{name: "Ranjan", age: 30}, {name: "Bob", age: 28}]; var battles = from var personA in teamA from var personB in teamB select string `${personA.name} vs ${personB.name}`; io:println(battles); } ================================================ FILE: examples/nested-query-expressions/nested_query_expressions.md ================================================ # Nested query expressions Intermediate clauses of a query expression can contain another query expression and there is no limit to the amount of such nested query expressions. This is similar to nested `foreach` statements. Nested query expressions are useful to create complex query expressions. Execution will happen according to the respective clauses. ::: code nested_query_expressions.bal ::: ::: out nested_query_expressions.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) ================================================ FILE: examples/nested-query-expressions/nested_query_expressions.metatags ================================================ description: This BBE demonstrates multiple query expressions, nested query expressions, and inner query expressions. keywords: ballerina, ballerina by example, bbe, nested, query, join, inner ================================================ FILE: examples/nested-query-expressions/nested_query_expressions.out ================================================ $ bal run nested_query_expressions.bal ["Alex vs Ranjan","Alex vs Bob","John vs Ranjan","John vs Bob"] ================================================ FILE: examples/never-type/never_type.bal ================================================ import ballerina/io; type Pair record { int x; int y; }; Pair p = { x: 1, y: 2, "color": "blue" }; // This is a record with two `x` and `y` optional fields of the `never` type. // However, as you cannot have values of the `never` type, // the net result is that you cannot have the `x` and `y`fields in the record. type PairRest record { never x?; never y?; }; // The function always panics. It never returns normally. // Therefore, the return type can be defined as `never`. function whoops() returns never { panic error("whoops"); } public function main() { // `xml` describes the XML type that has no constituents. // Therefore `x` is an empty XML sequence. xml x = xml ``; io:println(x); // The `rest` variable contains the fields in `p` other than `x` and `y`. var {x: _, y: _, ...rest} = p; // Type of `rest` is a subtype of `PairRest`. PairRest pairRest = rest; io:println(pairRest); // Call the function that always panics. whoops(); } ================================================ FILE: examples/never-type/never_type.md ================================================ # Never type The `never` type is the type to which no value belongs. ::: code never_type.bal ::: ::: out never_type.out ::: ================================================ FILE: examples/never-type/never_type.metatags ================================================ description: This BBE demonstrates the `never` type in Ballerina keywords: ballerina, ballerina by example, bbe, never, never type ================================================ FILE: examples/never-type/never_type.out ================================================ bal run never_type.bal {"color":"blue"} error: whoops at never_type:whoops(never_type.bal:25) never_type:main(never_type.bal:40) ================================================ FILE: examples/nil/nil.bal ================================================ import ballerina/io; // Here type `int?` indicates that the value of `v` can be an `int` or `()`. int? v = (); // Here the value of `n` cannot be `()`. int? n = v == () ? 0 : v; // Elvis operator `x ?: y` returns `x` if it is not `nil` and `y` otherwise. int m = v ?: 0; // Falling off the end of a function or `return` by itself is equivalent to `return ()`. function foo() returns () { return (); } // Leaving off return type is equivalent to `returns ()`. public function main() { io:println(v); } ================================================ FILE: examples/nil/nil.md ================================================ # Nil Ballerina's version of `null` is called nil and written as `()`. Ballerina types do not implicitly allow nil. Type `T?` means `T` or nil. You can use `==` and `!=` to test whether a value is nil: no implicit conversion to `boolean`. ::: code nil.bal ::: ::: out nil.out ::: ================================================ FILE: examples/nil/nil.metatags ================================================ description: This BBE introduces the nil type in Ballerina. keywords: ballerina, ballerina by example, bbe, nil, null ================================================ FILE: examples/nil/nil.out ================================================ $ bal run nil.bal ================================================ FILE: examples/object/object.bal ================================================ import ballerina/io; class Engineer { string name; function init(string name) { self.name = name; } function getName() returns string { return self.name; } } public function main() { // Apply the `new` operator with a `class` to get an `object` value. Engineer engineer = new Engineer("Alice"); // Call the `getName` method using the `obj.method(args)` syntax. string engineerName = engineer.getName(); io:println(engineerName); // Accessing the `name` field using the `obj.field` syntax. engineerName = engineer.name; io:println(engineerName); } ================================================ FILE: examples/object/object.md ================================================ # Object The `object` is a basic data type in Ballerina. An object value has named methods and fields and these methods and fields share the same symbol space. This means that it is not possible for an object to have both a field and a method with the same name. A `class` is used to define an object type and provides a way to construct an object. The `new` expression is used to create an object from a `class` definition. ::: code object.bal ::: ::: out object.out ::: ## Related links - [Defining classes](/learn/by-example/defining-classes/) - [Object constructor](/learn/by-example/object-constructor/) - [Object value from class definition](/learn/by-example/object-value-from-class-definition/) - [Visibility of object fields and methods](/learn/by-example/visibility-of-object-fields-and-methods/) - [Object types](/learn/by-example/object-types/) - [Object closure](/learn/by-example/object-closure/) ================================================ FILE: examples/object/object.metatags ================================================ description: This BBE demonstrates defining a class, creating an object from the class definition, invoking object methods, and accessing object fields in Ballerina. keywords: ballerina, ballerina by example, bbe, object, class, new ================================================ FILE: examples/object/object.out ================================================ $ bal run object.bal Alice Alice ================================================ FILE: examples/object-closure/object_closure.bal ================================================ import ballerina/io; public function main() { string[] names = ["Ana", "Alice", "Bob"]; // Create an object value using an object constructor. var engineer = object { string name = ""; function setName(string name) { // Access the `names` variable as a closure within the `setName` method in the object constructor. names.push(name); self.name = name; } }; engineer.setName("Walter"); io:println(engineer.name); io:println(names); } ================================================ FILE: examples/object-closure/object_closure.md ================================================ # Object closure The object constructor and function can work as a closure, which can access variables outside of its own scope. ::: code object_closure.bal ::: ::: out object_closure.out ::: ## Related links - [Function closure](/learn/by-example/function-closure/) - [Object constructor](/learn/by-example/object-constructor/) ================================================ FILE: examples/object-closure/object_closure.metatags ================================================ description: This BBE demonstrates object closures in Ballerina. keywords: ballerina, ballerina by example, bbe, object closure, object constructor, self ================================================ FILE: examples/object-closure/object_closure.out ================================================ $ bal run object_closure.bal Walter ["Ana","Alice","Bob","Walter"] ================================================ FILE: examples/object-constructor/object_constructor.bal ================================================ import ballerina/io; public function main() { // Create an object value using the object constructor. var engineer = object { string name; // The `init` function in the object constructor can not have parameters. function init() { self.name = ""; } function setName(string name) { self.name = name; } function getName() returns string { return self.name; } }; engineer.setName("Alice"); io:println(engineer.getName()); } ================================================ FILE: examples/object-constructor/object_constructor.md ================================================ # Object constructor In Ballerina, there are two ways to construct an object value with named methods and fields: using an object constructor or using the `new` expression with a `class` definition. The `init` method in an object constructor cannot have parameters. ::: code object_constructor.bal ::: ::: out object_constructor.out ::: ## Related links - [Object value from class definition](/learn/by-example/object-value-from-class-definition/) - [Object types](/learn/by-example/object-types/) ================================================ FILE: examples/object-constructor/object_constructor.metatags ================================================ description: This BBE demonstrates constructing an object and inferring the object type in Ballerina. keywords: ballerina, ballerina by example, bbe, object, object constructor, var ================================================ FILE: examples/object-constructor/object_constructor.out ================================================ $ bal run object_constructor.bal Alice ================================================ FILE: examples/object-type-inclusion/object_type_inclusion.bal ================================================ import ballerina/io; type Cloneable object { function clone() returns Cloneable; }; type Person object { // The `Cloneable` object type is included as a part of the interface of // the `Person` object type. *Cloneable; string name; // `getName()` is a part of the `Person`'s own type. // The `clone()` function is also included from the `Cloneable` type. function getName() returns string; }; class Engineer { // The `Engineer` class includes the `Person` object type. // Therefore, it has to implement both the `clone()` and `getName()` methods. *Person; function init(string name) { // The `name` field is included from the `Person` type. self.name = name; } // Returning `Engineer` is valid as the `Engineer` type becomes a subtype of the `Cloneable` type // once it includes the `Cloneable` object type. function clone() returns Engineer { return new (self.name); } function getName() returns string { return self.name; } } public function main() { Engineer engineer = new Engineer("Alice"); io:println(engineer.getName()); Engineer engineerClone = engineer.clone(); io:println(engineerClone.getName()); io:println(engineer === engineerClone); } ================================================ FILE: examples/object-type-inclusion/object_type_inclusion.md ================================================ # Object type inclusion Object type inclusion enables two things. Firstly, it allows the creation of an object type that includes another object type such that one interface extends another interface. Secondly, it allows creating a class that includes a type like the class implementing the interface. You can include object types using the `*T` syntax. The implementation of the object type within the class that includes the type is checked at the compile time. ::: code object_type_inclusion.bal ::: ::: out object_type_inclusion.out ::: ## Related links - [Object types](/learn/by-example/object-types/) - [Defining classes](/learn/by-example/defining-classes/) - [Type inclusion for records](/learn/by-example/type-inclusion-for-records/) ================================================ FILE: examples/object-type-inclusion/object_type_inclusion.metatags ================================================ description: This BBE demonstrates including an object type into another object type, a class implementing an interface, and defining a class in Ballerina. keywords: ballerina, ballerina by example, bbe, object types, type inclusion, object type descriptor, class, methods, inheritance, interface ================================================ FILE: examples/object-type-inclusion/object_type_inclusion.out ================================================ $ bal run object_type_inclusion.bal Alice Alice false ================================================ FILE: examples/object-types/object_types.bal ================================================ import ballerina/io; // The `Hashable` object type with a method called `hash()`, which returns an integer. type Hashable object { function hash() returns int; }; function h() returns any { var obj = object { // Implements the `hash()` method defined in the `Hashable` object type. function hash() returns int { return 42; } // The object can have other methods. function zero() returns int { return 0; } }; return obj; } public function main() { // The returned object matches the pattern of the `Hashable` object type, // which contains a `hash()` method returning an integer. io:println(h() is Hashable); } ================================================ FILE: examples/object-types/object_types.md ================================================ # Object types An object type is a type definition without any implementation. It is similar to an interface as defined in the Java programming language. Object typing is structural and an object type looks like a pattern that the object must match. ::: code object_types.bal ::: ::: out object_types.out ::: ================================================ FILE: examples/object-types/object_types.metatags ================================================ description: This BBE demonstrates object types in Ballerina. keywords: ballerina, ballerina by example, bbe, object types, object, type ================================================ FILE: examples/object-types/object_types.out ================================================ $ bal run object_types.bal true ================================================ FILE: examples/object-value-from-class-definition/object_value_from_class_definition.bal ================================================ import ballerina/io; class Engineer { string name; function init(string name = "Null") { self.name = name; } function getName() returns string { return self.name; } } public function main() { // Explicit new expression. // Object is initialized using the default value in the `init` method. Engineer engineer1 = new Engineer(); io:println(engineer1.getName()); // Object is initialized by passing an argument to the class descriptor with the new expression. Engineer engineer2 = new Engineer("Alice"); io:println(engineer2.getName()); // Object is initialized by passing a named argument to // the class descriptor with the new expression. Engineer engineer3 = new Engineer(name = "Bob"); io:println(engineer3.getName()); // Implicit new expression. // Object is initialized using the default value in the `init` method. Engineer engineer4 = new; io:println(engineer4.getName()); // Object is initialized by passing an argument to the new expression. Engineer engineer5 = new ("Alice"); io:println(engineer5.getName()); // Object is initialized by passing a named argument to the new expression. Engineer engineer6 = new (name = "Bob"); io:println(engineer6.getName()); } ================================================ FILE: examples/object-value-from-class-definition/object_value_from_class_definition.md ================================================ # Object value from class definition A `class` defines an object type and provides a way to construct an object value. An object can be constructed from the `class` definition using the implicit and explicit `new` expression. The type of the constructor should be mentioned after `new` for the explicit new expression. For the implicit new expression, the type will be inferred from the expected type. ::: code object_value_from_class_definition.bal ::: ::: out object_value_from_class_definition.out ::: ## Related links - [Object constructor](/learn/by-example/object-constructor/) - [Defining classes](/learn/by-example/defining-classes/) ================================================ FILE: examples/object-value-from-class-definition/object_value_from_class_definition.metatags ================================================ description: This BBE demonstrates constructing an object using a new expression and defining classes in Ballerina. keywords: ballerina, ballerina by example, bbe, class, methods, new, object ================================================ FILE: examples/object-value-from-class-definition/object_value_from_class_definition.out ================================================ $ bal run object_value_from_class_definition.bal Null Alice Bob Null Alice Bob ================================================ FILE: examples/on-conflict-clause/on_conflict_clause.bal ================================================ import ballerina/io; type Student record {| readonly int id; string name; int score; |}; public function main() { error onConflictError = error("Key Conflict", message = "record with same key exists."); table students = table [ {id: 1, name: "John", score: 100}, {id: 2, name: "Jane", score: 150}, {id: 1, name: "John", score: 200} ]; // The result of the following will be an error since the key `John` is duplicated. map|error studentScores = map from var {name, score} in students select [name, score] on conflict onConflictError; io:println(studentScores); // The value `100` of the key `John` will be replaced by the second occurrence value `200`. map lastRoundScore = map from var student in students select [student.name, student.score] on conflict (); io:println(lastRoundScore); // The result of the following will be an error since the key `1` is duplicated. table key(id)|error studentScoresTable = table key(id) from var student in students select student on conflict onConflictError; io:println(studentScoresTable); } ================================================ FILE: examples/on-conflict-clause/on_conflict_clause.md ================================================ # `on conflict` clause When constructing a map or a table with a key sequence using a query expression, there can be conflicting keys. The `on conflict` clause can be specified after the `select` clause to handle these cases. The `on conflict` clause uses the syntax `on conflict `, where the expression must be of type `error?`. If a conflicting key is found, and the expression evaluates to an error, that error becomes the result of the query expression. If it evaluates to `null` or the `on conflict` clause is absent, the old value is replaced by the new value. ::: code on_conflict_clause.bal ::: ::: out on_conflict_clause.out ::: ## Related links - [Advanced conflict handling](/learn/by-example/advanced-conflict-handling) - [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/on-conflict-clause/on_conflict_clause.metatags ================================================ description: This BBE demonstrates handling duplicate keys when constructing a map or a table. keywords: ballerina, ballerina by example, bbe, duplicate, key, map, table, query ================================================ FILE: examples/on-conflict-clause/on_conflict_clause.out ================================================ $ bal run on_conflict_clause.bal error("Key Conflict",message="record with same key exists.") {"John":200,"Jane":150} error("Key Conflict",message="record with same key exists.") ================================================ FILE: examples/open-records/open_records.bal ================================================ import ballerina/io; // `Student` type allows additional fields with `anydata` values. type Student record { string name; int age; }; type PartTimeStudent record {| string name; int age; // Rest descriptor allows additional fields with `anydata` values // in the `PartTimeStudent` type. anydata...; |}; public function main() { // Adds an additional `country` field to `s1`. Student s1 = { name: "John", age: 25, "country": "UK" }; io:println(s1); // Accesses the `age` field in `s1`. int age = s1.age; io:println(age); // Accesses the `country` field in `s1`. anydata country = s1["country"]; io:println(country); // Adds an additional `studyHours` field to `s2`. PartTimeStudent s2 = { name: "Anne", age: 23, "studyHours": 6 }; // Accesses the `studyHours` field in `s2`. anydata studyHours = s2["studyHours"]; io:println(studyHours); // Adds an additional `credits` field to `s2`. s2["credits"] = 120.5; io:println(s2); // A variable of type `PartTimeStudent` can be used where a `Student` value is expected. Student s3 = s2; io:println(s3); // A variable of type `Student` can be used where a `map` value is expected. map s4 = s3; io:println(s4); } ================================================ FILE: examples/open-records/open_records.md ================================================ # Open records A record type that uses either the `{` and `}` delimiters or the `{|` and `|}` delimiters with a rest descriptor is considered open. Open records allow fields other than those explicitly specified. When the record is open due to the use of the `{` and `}` delimiters, the expected type for any additional field is `anydata`. When the record is open due to the use of a rest descriptor, the expected type for any additional field is the type specified in the rest descriptor. Quoted keys can be used to specify fields that are not explicitly specified in an open record type. ::: code open_records.bal ::: ::: out open_records.out ::: ## Related links - [Records](/learn/by-example/records/) - [Controlling openness](/learn/by-example/controlling-openness/) - [Maps](/learn/by-example/maps/) - [Anydata type](/learn/by-example/anydata-type/) ================================================ FILE: examples/open-records/open_records.metatags ================================================ description: This BBE demonstrates open records, creating an open record, inclusive record type, creating a record with unspecified fields, and accesing rest fields in a record. keywords: ballerina, ballerina by example, bbe, open record, record, record type, rest type, rest field, inclusive record, unspecified fields ================================================ FILE: examples/open-records/open_records.out ================================================ $ bal run open_records.bal {"name":"John","age":25,"country":"UK"} 25 UK 6 {"name":"Anne","age":23,"studyHours":6,"credits":120.5} {"name":"Anne","age":23,"studyHours":6,"credits":120.5} {"name":"Anne","age":23,"studyHours":6,"credits":120.5} ================================================ FILE: examples/optional-fields/optional_fields.bal ================================================ import ballerina/io; type FullName record { string firstName; string lastName; // The `title` and `middleName` fields are optional. string title?; string middleName?; }; public function main() { FullName name = { title: "Mr", firstName: "John", lastName: "Doe" }; // Use the `?.` operator to access the optional field. io:println("Title: ", name?.title); // A variable of type `string?` is used to construct an optional field. string? middleName = name["middleName"]; io:println("Middle name: ", middleName); // Remove the optional `title` field by assigning `()`. name.title = (); io:println(name.hasKey("title")); // When destructuring the record `name`, if the `title` field is absent, // then, its value becomes nil. var {title, firstName: _, lastName: _} = name; io:println(title is ()); } ================================================ FILE: examples/optional-fields/optional_fields.md ================================================ # Optional fields Fields of a `record` type can be marked as optional. These fields can be omitted when creating a value of the record type. A field `f` of record type `r` can be accessed via optional field access (e.g., `r?.f`) or member access (e.g., `r["f"]`), which will both return `()` if the field is not present in the record value. A record that contains optional fields can be destructured. If the optional field is not available, the type of the variable becomes `nil`. The optional field value can also be removed from the record by assigning `()` to the optional field. ::: code optional_fields.bal ::: ::: out optional_fields.out ::: ## Related links - [Records](/learn/by-example/records/) ================================================ FILE: examples/optional-fields/optional_fields.metatags ================================================ description: This BBE demonstrates optional record fields by defining a field without a value, destructuring a record with optional fields, and removing the optional field value from a record in Ballerina. keywords: ballerina, ballerina by example, bbe, optional, fields, record ================================================ FILE: examples/optional-fields/optional_fields.out ================================================ $ bal run optional_fields.bal Title: Mr Middle name: false true ================================================ FILE: examples/outer-join-clause/outer_join_clause.bal ================================================ import ballerina/io; type Department record {| int id; string name; |}; type Person record {| int id; string fname; string lname; |}; type DeptPerson record {| string fname; string? dept; |}; public function main() { Person p1 = {id: 1, fname: "Alex", lname: "George"}; Person p2 = {id: 2, fname: "John", lname: "Fonseka"}; Person p3 = {id: 3, fname: "Ted", lname: "Perera"}; Department d1 = {id: 1, name:"HR"}; Department d2 = {id: 2, name:"Operations"}; Person[] personList = [p1, p2, p3]; Department[] deptList = [d1, d2]; DeptPerson[] deptPersonList = from Person person in personList // The `outer join` clause performs a left outer equijoin. // For the records for which there is no matching record // on the `deptList`, the resulting record will contain `nil` fields. outer join var dept in deptList on person.id equals dept?.id select { fname : person.fname, dept : dept?.name }; io:println(deptPersonList); } ================================================ FILE: examples/outer-join-clause/outer_join_clause.md ================================================ # Outer join clause The `outer join` clause performs a left outer equijoin. This join returns each element of the first collection regardless of whether it has any matching elements in the second collection. For the values for which there is no matching value on the second collection, the resulting value will contain `nil`. ::: code outer_join_clause.bal ::: ::: out outer_join_clause.out ::: ================================================ FILE: examples/outer-join-clause/outer_join_clause.metatags ================================================ description: This BBE demonstrates the outer join clause in Ballerina. keywords: ballerina, ballerina by example, bbe, outer join clause, query, query expression ================================================ FILE: examples/outer-join-clause/outer_join_clause.out ================================================ $ bal run outer_join_clause.bal [{"fname":"Alex","dept":"HR"},{"fname":"John","dept":"Operations"},{"fname":"Ted","dept":null}] ================================================ FILE: examples/panics/panics.bal ================================================ import ballerina/io; // `n` must not be `0`. function divide(int m, int n) returns int { if n == 0 { // Panic if `n` is `0`. panic error("division by 0"); } return m / n; } public function main() { // Even though `divide(1, 0)` panics, due to the trap expression, the program will not terminate here. int|error x = trap divide(1, 0); if x is error { io:println(x); } int y = divide(1, 0); // If `divide(1, 0)` panics, the program will terminate and the following code will not be // executed. io:println(y); } ================================================ FILE: examples/panics/panics.md ================================================ # Panics Ballerina distinguishes normal errors from abnormal errors. Normal errors are handled by returning error values. This signals to the caller that they must handle the error. In contrast, abnormal errors such as division by 0 and out-of-memory are typically unrecoverable errors and we need to terminate the execution of the program. This is achieved by a panic statement with an expression that results in an error value such as the error constructor. At runtime, evaluating a panic statement first creates the error value by evaluating the expression and then starts stack unwinding, unless it encounters a `trap` expression. ::: code panics.bal ::: ::: out panics.out ::: ================================================ FILE: examples/panics/panics.metatags ================================================ description: This BBE demonstrates how abnormal errors are handled using panics in Ballerina keywords: ballerina, ballerina by example, bbe, error, panic ================================================ FILE: examples/panics/panics.out ================================================ $ bal run panics.bal error("division by 0") error: division by 0 at panics:divide(panics.bal:8) panics:main(panics.bal:23) ================================================ FILE: examples/parse-csv-lists/parse_csv_lists.bal ================================================ import ballerina/data.csv; import ballerina/io; // Represents the details of an employee. type Employee record {| int empId; string empName; string department; float salary; |}; // Represents the salary details of an employee. type EmployeeSalary record {| // This annotation is used to map the empId field in the source CSV record // to the id field in the target record. @csv:Name { value: "empId" } int id; float salary; |}; public function main() returns error? { // Sample CSV string array representing employee data. string[][] csvList = [ ["1", "John", "Engineering", "1000.0"], ["2", "Doe", "HR", "2000.0"], ["3", "Jane", "Finance", "3000.0"] ]; // Parse the CSV list into an array of `Employee` records by specifying custom headers. Employee[] employees = check csv:parseList(csvList, { customHeaders: ["empId", "empName", "department", "salary"] }); io:println(employees); // Parse the CSV list into an array of `EmployeeSalary` records by specifying custom headers. // Only the fields specified in the EmployeeSalary type (`id` and `salary`) // are included in the resulting array. EmployeeSalary[] employeeSalaries = check csv:parseList(csvList, { customHeaders: ["empId", "empName", "department", "salary"] }); io:println(employeeSalaries); // Parse the CSV list into an array of `anydata` arrays. anydata[][] employeesArray = check csv:parseList(csvList); io:println(employeesArray); } ================================================ FILE: examples/parse-csv-lists/parse_csv_lists.md ================================================ # Parse CSV lists to custom types Ballerina enables parsing CSV lists into various data structures, including custom `record` types and arrays. It facilitates parsing CSV lists into record types with specified fields, allowing for both reshaping of the data and maintaining its original structure. ::: code parse_csv_lists.bal ::: ::: out parse_csv_lists.out ::: ================================================ FILE: examples/parse-csv-lists/parse_csv_lists.metatags ================================================ description: This BBE demonstrates the parsing of CSV lists into Ballerina records and arrays, both with and without data projection. keywords: ballerina, ballerina by example, bbe, csv, csv lists, record, record array, parseList, csv data module, data.csv, data projection, csv to record array ================================================ FILE: examples/parse-csv-lists/parse_csv_lists.out ================================================ $ bal run parse_csv_lists.bal [{"empId":1,"empName":"John","department":"Engineering","salary":1000.0},{"empId":2,"empName":"Doe","department":"HR","salary":2000.0},{"empId":3,"empName":"Jane","department":"Finance","salary":3000.0}] [{"id":1,"salary":1000.0},{"id":2,"salary":2000.0},{"id":3,"salary":3000.0}] [[1,"John","Engineering",1000.0],[2,"Doe","HR",2000.0],[3,"Jane","Finance",3000.0]] ================================================ FILE: examples/persist-create/persist_create.bal ================================================ import ballerina/io; import ballerina/persist; import rainier.store; store:Client sClient = check new (); public function main() returns error? { store:EmployeeInsert employee1 = { id: uuid:createType4AsString(), firstName: "John", lastName: "Doe", gender: "Male", birthDate: { year: 1987, month: 7, day: 23 }, hireDate: { year: 2020, month: 10, day: 10 } }; store:EmployeeInsert employee2 = { id: uuid:createType4AsString(), firstName: "Jane", lastName: "Doe", gender: "Female", birthDate: { year: 1989, month: 7, day: 11 }, hireDate: { year: 2020, month: 10, day: 10 } }; string[] employeeIds = check sClient->/employees.post([employee1, employee2]); io:println(string `Inserted employee ids: ${employeeIds.toString()}`); } ================================================ FILE: examples/persist-create/persist_create.md ================================================ # Persist write - Create record/records The bal persist feature provides support to manage data persistence in a Ballerina package. It starts with defining the application's data model. Once the model is defined, the client API is generated with resources based on the model. The generated API can be used to query and manipulate the persistent data in the application. The generated client API provides a `post` resource method to persist record/records to the data store. > **Note:** This example uses the Ballerina tables as the data store. For more information about other supported data stores, see [Supported Data Stores](/learn/supported-data-stores/). #### Initialize the project Execute the command below to initialize `bal persist` in the project. ::: out persist_init.out ::: #### Model the data Add the `Employee` entity with the following fields in the `model.bal` file inside the `persist` directory. ::: code persist_model.bal ::: #### Generate client APIs Execute the command below to generate the Ballerina client API. ::: out persist_generate.out ::: > **Note:** The `bal persist generate` command is a one-time generation task, and the generated client code is a part of the project. We can also integrate the client code generation with the project's build process by executing the `bal persist add` command. This command will add the client code generation as a build task in the `Ballerina.toml` file. See [Persist CLI Commands](learn/persist-cli-tool/) for more information. #### Use the generated client API Using the generated client API, you can persist record/records to the data store with the `post` resource method. ::: code persist_create.bal ::: #### Run the program Execute the command below to run the program. ::: out persist_create.out ::: ================================================ FILE: examples/persist-create/persist_create.metatags ================================================ description: This BBE demonstrates the usage of post resource method in the generated persistence client to persist record/records to the data store keywords: ballerina, ballerina by example, bbe, persistence, data store, post, entity ================================================ FILE: examples/persist-create/persist_create.out ================================================ $ bal run Compiling source foo/rainier:0.1.0 Running executable Inserted employee ids: ["394c694f-82ae-4fe9-9947-85948625fea1","639082b7-4be7-4a64-8db2-1e3d1b3313f8"] ================================================ FILE: examples/persist-create/persist_generate.out ================================================ $ bal persist generate --datastore inmemory --module store Persist client and entity types generated successfully in the store directory. ================================================ FILE: examples/persist-create/persist_init.out ================================================ $ bal persist init Initialized the package for persistence. Next steps: 1. Define your data model in "persist/model.bal". 2. Execute `bal persist add --datastore --module ` to add an entry to "Ballerina.toml" for integrating code generation with the package build process. OR Execute `bal persist generate --datastore --module ` for a one-time generation of the client. ================================================ FILE: examples/persist-create/persist_model.bal ================================================ import ballerina/persist as _; import ballerina/time; type Employee record {| readonly string id; string firstName; string lastName; time:Date birthDate; string gender; time:Date hireDate; |}; ================================================ FILE: examples/persist-delete/persist_create.metatags ================================================ description: This BBE demonstrates the usage of delete resource method in the generated persistence client to delete record from the data store. keywords: ballerina, ballerina by example, bbe, persistence, data store, delete, entity ================================================ FILE: examples/persist-delete/persist_delete.bal ================================================ import ballerina/io; import ballerina/persist; import rainier.store; store:Client sClient = check new (); public function main() returns error? { string empId = "16c6553a-373c-4b29-b1c8-c282f444248c"; // Delete employee record with id 16c6553a-373c-4b29-b1c8-c282f444248c store:Employee employee = check sClient->/employees/[empId].delete(); io:println(string `Deleted employee record: ${employee.toString()}`); } ================================================ FILE: examples/persist-delete/persist_delete.md ================================================ # Persist write - Delete record The bal persist feature provides support to manage data persistence in a Ballerina package. It starts with defining the application's data model. Once the model is defined, the client API is generated with resources based on the model. The generated API can be used to query and manipulate the persistent data in the application. The generated client API provides a `delete` resource method to delete records from the data store. > **Note:** This example uses the Ballerina tables as the data store. For more information about other supported data stores, see [Supported Data Stores](/learn/supported-data-stores/). #### Initialize the project Execute the command below to initialize `bal persist` in the project. ::: out persist_init.out ::: #### Model the data Add `Employee` entity with the following fields in the `model.bal` file inside the `persist` directory. ::: code persist_model.bal ::: #### Generate client APIs Execute the command below to generate the Ballerina client API. ::: out persist_generate.out ::: > **Note:** The `bal persist generate` command is a one-time generation task, and the generated client code is a part of the project. We can also integrate the client code generation with the project's build process by executing the `bal persist add` command. This command will add the client code generation as a build task in the `Ballerina.toml` file. See [Persist CLI Commands](learn/persist-cli-tool/) for more information. #### Use the generated client API Using the generated client API, you can delete records from the data store with the `delete` resource method. ::: code persist_delete.bal ::: #### Run the program Execute the command below to run the program. ::: out persist_delete.out ::: ================================================ FILE: examples/persist-delete/persist_delete.out ================================================ $ bal run Compiling source foo/rainier:0.1.0 Running executable Deleted employee record: {"id":"16c6553a-373c-4b29-b1c8-c282f444248c","firstName":"John","lastName":"Doe","birthDate":{"year":1987,"month":07,"day":23},"gender":"Male","hireDate":{"year":2020,"month":10,"day":10}} ================================================ FILE: examples/persist-delete/persist_generate.out ================================================ $ bal persist generate --datastore inmemory --module store Persist client and entity types generated successfully in the store directory. ================================================ FILE: examples/persist-delete/persist_init.out ================================================ $ bal persist init Initialized the package for persistence. Next steps: 1. Define your data model in "persist/model.bal". 2. Execute `bal persist add --datastore --module ` to add an entry to "Ballerina.toml" for integrating code generation with the package build process. OR Execute `bal persist generate --datastore --module ` for a one-time generation of the client. ================================================ FILE: examples/persist-delete/persist_model.bal ================================================ import ballerina/persist as _; import ballerina/time; type Employee record {| readonly string id; string firstName; string lastName; time:Date birthDate; string gender; time:Date hireDate; |}; ================================================ FILE: examples/persist-filtering/persist_filtering.bal ================================================ import ballerina/io; import ballerina/persist; import rainier.store; // A record with subset of fields type EmployeeName record {| string id; string firstName; string lastName; |}; store:Client sClient = check new (); public function main() returns error? { // Filter Employee records by gender stream employees = sClient->/employees; store:Employee[] filtered = check from var employee in employees where employee.gender == "Male" select employee; foreach store:Employee e in filtered { io:println(e); } // Order by Employee records by first name in ascending order, and then by last name in descending order stream empNames = sClient->/employees; EmployeeName[] sorted = check from var e in empNames order by e.firstName ascending, e.lastName descending select e; foreach EmployeeName e in sorted { io:println(e.firstName, " ", e.lastName); } // Limit the results to 2 employees = sClient->/employees; store:Employee[] limited = check from var employee in employees order by employee.age descending limit 2 select employee; foreach store:Employee e in limited { io:println(e); } } ================================================ FILE: examples/persist-filtering/persist_filtering.md ================================================ # Persist read - Filter and sort The bal persist feature provide support to manage data persistence in a Ballerina package. It starts with defining the application's data model. Once model is defined, the client API is generated with resources based on the model. The generated API can be used to query and manipulate the persistent data in the application. The generated client API provides `get` resource method to retrieve all records from the data store. As the `get` resource method returns a stream of records, we can use query expressions to do additional filter. For more information, see [Query Expressions](/learn/by-example/query-expressions/). > **Note:** This example uses the Ballerina tables as the data store. For more information about other supported data stores, see [Supported Data Stores](/learn/supported-data-stores/). #### Initialize the project Execute the command below to initialize `bal persist` in the project. ::: out persist_init.out ::: #### Model the data Add `Employee` entity with the following fields in the `model.bal` file inside the `persist` directory. ::: code persist_model.bal ::: #### Generate client APIs Execute the command below to generate the Ballerina client API. ::: out persist_generate.out ::: > **Note:** The `bal persist generate` command is a one-time generation task, and the generated client code is a part of the project. We can also integrate the client code generation with the project's build process by executing the `bal persist add` command. This command will add the client code generation as a build task in the `Ballerina.toml` file. See [Persist CLI Commands](learn/persist-cli-tool/) for more information. #### Use the generated client API Using the generated client API, we can retrieve all records from the data store. The `get` resource method returns a stream of records. We can iterate through the stream and print the records. ::: code persist_filtering.bal ::: #### Run the program Execute the command below to run the program. ::: out persist_filtering.out ::: ================================================ FILE: examples/persist-filtering/persist_filtering.metatags ================================================ description: This BBE demonstrates the usage of query expressions with get resource method in generated persistence client. keywords: ballerina, ballerina by example, bbe, persistence, data store, get, entity, filtering, query ================================================ FILE: examples/persist-filtering/persist_filtering.out ================================================ $ bal run Compiling source foo/rainier:0.1.0 Running executable {"id":"581d1bc2-0390-452a-a564-4219e66af395","firstName":"Sam","lastName":"Curran","birthDate":{"year":1993,"month":7,"day":23},"gender":"Male","age":27,"hireDate":{"year":2020,"month":10,"day":10}} {"id":"9ae73215-207d-4e62-8596-5f2fd47ff7d5","firstName":"John","lastName":"Doe","birthDate":{"year":1987,"month":7,"day":23},"gender":"Male","age":33,"hireDate":{"year":2020,"month":10,"day":10}} Jane Doe John Doe Sam Curran {"id":"9ae73215-207d-4e62-8596-5f2fd47ff7d5","firstName":"John","lastName":"Doe","birthDate":{"year":1987,"month":7,"day":23},"gender":"Male","age":33,"hireDate":{"year":2020,"month":10,"day":10}} {"id":"639082b7-4be7-4a64-8db2-1e3d1b3313f8","firstName":"Jane","lastName":"Doe","birthDate":{"year":1989,"month":7,"day":11},"gender":"Female","age":31,"hireDate":{"year":2020,"month":10,"day":10}} ================================================ FILE: examples/persist-filtering/persist_generate.out ================================================ $ bal persist generate --datastore inmemory --module store Persist client and entity types generated successfully in the store directory. ================================================ FILE: examples/persist-filtering/persist_init.out ================================================ $ bal persist init Initialized the package for persistence. Next steps: 1. Define your data model in "persist/model.bal". 2. Execute `bal persist add --datastore --module ` to add an entry to "Ballerina.toml" for integrating code generation with the package build process. OR Execute `bal persist generate --datastore --module ` for a one-time generation of the client. ================================================ FILE: examples/persist-filtering/persist_model.bal ================================================ import ballerina/persist as _; import ballerina/time; type Employee record {| readonly string id; string firstName; string lastName; int age; time:Date birthDate; string gender; time:Date hireDate; |}; ================================================ FILE: examples/persist-get-all/persist_generate.out ================================================ $ bal persist generate --datastore inmemory --module store Persist client and entity types generated successfully in the store directory. ================================================ FILE: examples/persist-get-all/persist_get_all.bal ================================================ import ballerina/io; import ballerina/persist; import rainier.store; store:Client sClient = check new (); public function main() returns error? { // Get entire Employee record stream employees = sClient->/employees; check from var employee in employees do { io:println(employee); }; } ================================================ FILE: examples/persist-get-all/persist_get_all.md ================================================ # Persist read - Get all records The bal persist feature provides support to manage data persistence in a Ballerina package. It starts with defining the application's data model. Once the model is defined, the client API is generated with resources based on the model. The generated API can be used to query and manipulate the persistent data in the application. The generated client API provides a `get` resource method to retrieve all records from the data store. > **Note:** This example uses the Ballerina tables as the data store. For more information about other supported data stores, see [Supported Data Stores](/learn/supported-data-stores/). #### Initialize the project Execute the command below to initialize `bal persist` in the project. ::: out persist_init.out ::: #### Model the data Add `Employee` entity with the following fields in the `model.bal` file inside the `persist` directory. ::: code persist_model.bal ::: #### Generate client APIs Execute the command below to generate the Ballerina client API. ::: out persist_generate.out ::: > **Note:** The `bal persist generate` command is a one-time generation task, and the generated client code is a part of the project. We can also integrate the client code generation with the project's build process by executing the `bal persist add` command. This command will add the client code generation as a build task in the `Ballerina.toml` file. See [Persist CLI Commands](learn/persist-cli-tool/) for more information. #### Use the generated client API Using the generated client API, we can retrieve all records from the data store. The `get` resource method returns a stream of records. We can iterate through the stream and print the records. ::: code persist_get_all.bal ::: #### Run the program Execute the command below to run the program. ::: out persist_get_all.out ::: ================================================ FILE: examples/persist-get-all/persist_get_all.metatags ================================================ description: This BBE demonstrates the usage of get resource method in generated persistence client. keywords: ballerina, ballerina by example, bbe, persistence, data store, get, entity ================================================ FILE: examples/persist-get-all/persist_get_all.out ================================================ $ bal run Compiling source foo/rainier:0.1.0 Running executable {"id":"394c694f-82ae-4fe9-9947-85948625fea1","firstName":"John","lastName":"Doe","birthDate":{"year":1987,"month":7,"day":23},"gender":"Male","age":33,"hireDate":{"year":2020,"month":10,"day":10}} {"id":"639082b7-4be7-4a64-8db2-1e3d1b3313f8","firstName":"Jane","lastName":"Doe","birthDate":{"year":1989,"month":7,"day":11},"gender":"Female","age":31,"hireDate":{"year":2020,"month":10,"day":10}} {"id":"581d1bc2-0390-452a-a564-4219e66af395","firstName":"Sam","lastName":"Curran","birthDate":{"year":1993,"month":7,"day":23},"gender":"Male","age":27,"hireDate":{"year":2020,"month":10,"day":10}} ================================================ FILE: examples/persist-get-all/persist_init.out ================================================ $ bal persist init Initialized the package for persistence. Next steps: 1. Define your data model in "persist/model.bal". 2. Execute `bal persist add --datastore --module ` to add an entry to "Ballerina.toml" for integrating code generation with the package build process. OR Execute `bal persist generate --datastore --module ` for a one-time generation of the client. ================================================ FILE: examples/persist-get-all/persist_model.bal ================================================ import ballerina/persist as _; import ballerina/time; type Employee record {| readonly string id; string firstName; string lastName; time:Date birthDate; string gender; time:Date hireDate; |}; ================================================ FILE: examples/persist-get-by-key/persist_generate.out ================================================ $ bal persist generate --datastore inmemory --module store Persist client and entity types generated successfully in the store directory. ================================================ FILE: examples/persist-get-by-key/persist_get_by_key.bal ================================================ import ballerina/io; import ballerina/persist; import rainier.store; store:Client sClient = check new (); public function main() returns error? { string empId = "16c6553a-373c-4b29-b1c8-c282f444248c"; // Get entire Employee record by key store:Employee employee = check sClient->/employees/[empId]; io:println(employee); } ================================================ FILE: examples/persist-get-by-key/persist_get_by_key.md ================================================ # Persist read - Get record by key The bal persist feature provides support to manage data persistence in a Ballerina package. It starts with defining the application's data model. Once the model is defined, the client API is generated with resources based on the model. The generated API can be used to query and manipulate the persistent data in the application. The generated client API provides `get by key` resource method to retrieve the record by the key from the data store. > **Note:** This example uses the Ballerina tables as the data store. For more information about other supported data stores, see [Supported Data Stores](/learn/supported-data-stores/). #### Initialize the project Execute the command below to initialize `bal persist` in the project. ::: out persist_init.out ::: #### Model the data Add `Employee` entity with the following fields in the `model.bal` file inside the `persist` directory. ::: code persist_model.bal ::: #### Generate client APIs Execute the command below to generate the Ballerina client API. ::: out persist_generate.out ::: > **Note:** The `bal persist generate` command is a one-time generation task, and the generated client code is a part of the project. We can also integrate the client code generation with the project's build process by executing the `bal persist add` command. This command will add the client code generation as a build task in the `Ballerina.toml` file. See [Persist CLI Commands](learn/persist-cli-tool/) for more information. #### Use the generated client API Using the generated client API, you can retrieve the record by key from the data store. The `get by key` resource method returns a record or error if no records are found for the given key. ::: code persist_get_by_key.bal ::: #### Run the program Execute the command below to run the program. ::: out persist_get_by_key.out ::: ================================================ FILE: examples/persist-get-by-key/persist_get_by_key.metatags ================================================ description: This BBE demonstrates the usage of get by key resource method in generated persistence client. keywords: ballerina, ballerina by example, bbe, persistence, data store, get by key, entity ================================================ FILE: examples/persist-get-by-key/persist_get_by_key.out ================================================ $ bal run Compiling source foo/rainier:0.1.0 Running executable {"id":"16c6553a-373c-4b29-b1c8-c282f444248c","firstName":"John","lastName":"Doe","birthDate":{"year":1987,"month":07,"day":23},"gender":"Male","hireDate":{"year":2020,"month":10,"day":10}} ================================================ FILE: examples/persist-get-by-key/persist_init.out ================================================ $ bal persist init Initialized the package for persistence. Next steps: 1. Define your data model in "persist/model.bal". 2. Execute `bal persist add --datastore --module ` to add an entry to "Ballerina.toml" for integrating code generation with the package build process. OR Execute `bal persist generate --datastore --module ` for a one-time generation of the client. ================================================ FILE: examples/persist-get-by-key/persist_model.bal ================================================ import ballerina/persist as _; import ballerina/time; type Employee record {| readonly string id; string firstName; string lastName; time:Date birthDate; string gender; time:Date hireDate; |}; ================================================ FILE: examples/persist-relation-queries/persist_generate.out ================================================ $ bal persist generate --datastore inmemory --module store Persist client and entity types generated successfully in the store directory. ================================================ FILE: examples/persist-relation-queries/persist_init.out ================================================ $ bal persist init Initialized the package for persistence. Next steps: 1. Define your data model in "persist/model.bal". 2. Execute `bal persist add --datastore --module ` to add an entry to "Ballerina.toml" for integrating code generation with the package build process. OR Execute `bal persist generate --datastore --module ` for a one-time generation of the client. ================================================ FILE: examples/persist-relation-queries/persist_model.bal ================================================ import ballerina/persist as _; import ballerina/time; type Employee record {| readonly string id; string firstName; string lastName; time:Date birthDate; string gender; time:Date hireDate; // one-to-many relationship with Department Department department; // one-to-one relationship with Workspace Workspace workspace; |}; type Workspace record {| readonly string id; string workspaceType; // one-to-many relationship with Building Building location; // one-to-one relationship with Employee Employee? employee; |}; type Department record {| readonly string no; string deptName; // one-to-many relationship with Employee Employee[] employees; |}; type Building record {| readonly string code; string city; string state; string country; string postalCode; string 'type; // one-to-many relationship with Workspace Workspace[] workspaces; |}; ================================================ FILE: examples/persist-relation-queries/persist_relation_queries.bal ================================================ import ballerina/io; import ballerina/persist; import rainier.store; // A record with relation fields - one-to-one relation type EmployeeWorkspace record {| string id; string firstName; string lastName; record {| string id; string workspaceType; |} workspace; |}; // A record with relation fields - one-to-many relation type DepartmentEmployees record {| string id; string name; record {| string id; string firstName; string lastName; |}[] employees; |}; store:Client sClient = check new (); public function main() returns error? { // Get record with relation fields for one-to-one relation stream empWorkspaceStream = sClient->/employees; check from var empWorkspace in empWorkspaceStream do { io:println(empWorkspace); }; // Get record with relation fields for one-to-many relation stream departmentStream = sClient->/departments; check from var department in departmentStream do { io:println(department); }; } ================================================ FILE: examples/persist-relation-queries/persist_relation_queries.md ================================================ # Persist read - Relation queries The bal persist feature provides support to manage data persistence in a Ballerina package. It starts with defining the application's data model. Once the model is defined, the client API is generated with resources based on the model. The generated API can be used to query and manipulate the persistent data in the application. The generated client API provides support to select fields in relational entities when retrieving the records/record from the `get` resource method from the data store. > **Note:** This example uses the Ballerina tables as the data store. For more information about other supported data stores, see [Supported Data Stores](/learn/supported-data-stores/). #### Initialize the project Execute the command below to initialize persistence in the project. ::: out persist_init.out ::: #### Model the data Add `Employee`, `Department`, `Workspace`, and `Building` entities with relations in between in the `model.bal` file inside the `persist` directory. ::: code persist_model.bal ::: #### Generate client APIs Execute the command below to generate the Ballerina client API. ::: out persist_generate.out ::: > **Note:** The `bal persist generate` command is a one-time generation task, and the generated client code is a part of the project. We can also integrate the client code generation with the project's build process by executing the `bal persist add` command. This command will add the client code generation as a build task in the `Ballerina.toml` file. See [Persist CLI Commands](learn/persist-cli-tool/) for more information. #### Use the generated client API Using the generated client API, you can retrieve the record with relations from the data store with both `get` and `get by key` resource methods. ::: code persist_relation_queries.bal ::: #### Run the program Execute the command below to run the program. ::: out persist_relation_queries.out ::: ================================================ FILE: examples/persist-relation-queries/persist_relation_queries.metatags ================================================ description: This BBE demonstrates the usage of the get/get by key resource method in generated persistence client to retrieve relation fields of an entity from the data store. keywords: ballerina, ballerina by example, bbe, persistence, data store, get by key, get, select, entity, relations ================================================ FILE: examples/persist-relation-queries/persist_relation_queries.out ================================================ $ bal run Compiling source foo/rainier:0.1.0 Running executable {"id":"1c09e131-364b-45f0-9f04-5e6a05bf8feb","firstName":"John","lastName":"Smith","workspace":{"id":"2c4696cf-a9a5-4b2e-be50-ccd3404394be","workspaceType":"Hot Desk"}} {"id":"4a5ae2c5-4df8-44b8-8da1-27585068071e","firstName":"Jane","lastName":"Doe","workspace":{"id":"b17f8735-f1b2-45fa-945a-2d9f5479af72","workspaceType":"Cubicle"}} {"id":"c53b2b5a-6763-43cf-ae61-de2fd5802eda","firstName":"John","lastName":"Doe","workspace":{"id":"3590c557-df63-4f90-bb60-23283655329b","workspaceType":"Cubicle"}} {"no":"D004","deptName":"Marketing","employees":[{"id":"4a5ae2c5-4df8-44b8-8da1-27585068071e","firstName":"Jane","lastName":"Doe"},{"id":"c53b2b5a-6763-43cf-ae61-de2fd5802eda","firstName":"John","lastName":"Doe"}]} {"no":"D005","deptName":"Sales","employees":[{"id":"1c09e131-364b-45f0-9f04-5e6a05bf8feb","firstName":"John","lastName":"Smith"}]} ================================================ FILE: examples/persist-select-fields/persist_generate.out ================================================ $ bal persist generate --datastore inmemory --module store Persist client and entity types generated successfully in the store directory. ================================================ FILE: examples/persist-select-fields/persist_init.out ================================================ $ bal persist init Initialized the package for persistence. Next steps: 1. Define your data model in "persist/model.bal". 2. Execute `bal persist add --datastore --module ` to add an entry to "Ballerina.toml" for integrating code generation with the package build process. OR Execute `bal persist generate --datastore --module ` for a one-time generation of the client. ================================================ FILE: examples/persist-select-fields/persist_model.bal ================================================ import ballerina/persist as _; import ballerina/time; type Employee record {| readonly string id; string firstName; string lastName; time:Date birthDate; string gender; time:Date hireDate; |}; ================================================ FILE: examples/persist-select-fields/persist_select_fields.bal ================================================ import ballerina/io; import ballerina/persist; import rainier.store; // A record with subset of fields type EmployeeName record {| string id; string firstName; string lastName; |}; store:Client sClient = check new (); public function main() returns error? { // Get only the employee_id, first_name and last_name fields of all the records stream empNames = sClient->/employees; check from var name in empNames do { io:println(name); }; // Get only the employee_id, first_name and last_name fields of a single record string empId = "16c6553a-373c-4b29-b1c8-c282f444248c"; EmployeeName employee = check sClient->/employees/[empId]; io:println(employee); } ================================================ FILE: examples/persist-select-fields/persist_select_fields.md ================================================ # Persist read - Select fields The bal persist feature provides support to manage data persistence in a Ballerina package. It starts with defining the application's data model. Once the model is defined, the client API is generated with resources based on the model. The generated API can be used to query and manipulate the persistent data in the application. The generated client API provides support to select a subset of fields of the entity when retrieving the records/record from the `get` resource method from the data store. > **Note:** This example uses the Ballerina tables as the data store. For more information about other supported data stores, see [Supported Data Stores](/learn/supported-data-stores/). #### Initialize the project Execute the command below to initialize `bal persist` in the project. ::: out persist_init.out ::: #### Model the data Add `Employee` entity with the following fields in the `model.bal` file inside the `persist` directory. ::: code persist_model.bal ::: #### Generate client APIs Execute the command below to generate the Ballerina client API. ::: out persist_generate.out ::: > **Note:** The `bal persist generate` command is a one-time generation task, and the generated client code is a part of the project. We can also integrate the client code generation with the project's build process by executing the `bal persist add` command. This command will add the client code generation as a build task in the `Ballerina.toml` file. See [Persist CLI Commands](learn/persist-cli-tool/) for more information. #### Use the generated client API Using the generated client API, you can retrieve the record with selected fields from the data store with both `get` and `get by key` resource methods. ::: code persist_select_fields.bal ::: #### Run the program Execute the command below to run the program. ::: out persist_select_fields.out ::: ================================================ FILE: examples/persist-select-fields/persist_select_fields.metatags ================================================ description: This BBE demonstrates the usage of the get/get by key resource method in generated persistence client to retrieve selected fields of an entity from the data store. keywords: ballerina, ballerina by example, bbe, persistence, data store, get by key, get, select, entity ================================================ FILE: examples/persist-select-fields/persist_select_fields.out ================================================ $ bal run Compiling source foo/rainier:0.1.0 Running executable {"id":"16c6553a-373c-4b29-b1c8-c282f444248c","firstName":"John","lastName":"Doe"} {"id":"16c6553a-373c-4b29-b1c8-c282f444248c","firstName":"John","lastName":"Doe"} ================================================ FILE: examples/persist-update/persist_generate.out ================================================ $ bal persist generate --datastore inmemory --module store Persist client and entity types generated successfully in the store directory. ================================================ FILE: examples/persist-update/persist_init.out ================================================ $ bal persist init Initialized the package for persistence. Next steps: 1. Define your data model in "persist/model.bal". 2. Execute `bal persist add --datastore --module ` to add an entry to "Ballerina.toml" for integrating code generation with the package build process. OR Execute `bal persist generate --datastore --module ` for a one-time generation of the client. ================================================ FILE: examples/persist-update/persist_model.bal ================================================ import ballerina/persist as _; import ballerina/time; type Employee record {| readonly string id; string firstName; string lastName; time:Date birthDate; string gender; time:Date hireDate; |}; ================================================ FILE: examples/persist-update/persist_update.bal ================================================ import ballerina/io; import ballerina/persist; import rainier.store; store:Client sClient = check new (); public function main() returns error? { string empId = "16c6553a-373c-4b29-b1c8-c282f444248c"; // Update hire date of employee with id 16c6553a-373c-4b29-b1c8-c282f444248c store:Employee employee = check sClient->/employees/[empId].put({ hireDate: { year: 2014, month: 5, day: 1 } }); io:println(string `Updated employee record: ${employee.toString()}`); } ================================================ FILE: examples/persist-update/persist_update.md ================================================ # Persist write - Update record The bal persist feature provides support to manage data persistence in a Ballerina package. It starts with defining the application's data model. Once the model is defined, the client API is generated with resources based on the model. The generated API can be used to query and manipulate the persistent data in the application. The generated client API provides a `put` resource method to update the record in the data store. > **Note:** This example uses the Ballerina tables as the data store. For more information about other supported data stores, see [Supported Data Stores](/learn/supported-data-stores/). #### Initialize the project Execute the command below to initialize `bal persist` in the project. ::: out persist_init.out ::: #### Model the data Add `Employee` entity with the following fields in the `model.bal` file inside the `persist` directory. ::: code persist_model.bal ::: #### Generate client APIs Execute the command below to generate the Ballerina client API. ::: out persist_generate.out ::: > **Note:** The `bal persist generate` command is a one-time generation task, and the generated client code is a part of the project. We can also integrate the client code generation with the project's build process by executing the `bal persist add` command. This command will add the client code generation as a build task in the `Ballerina.toml` file. See [Persist CLI Commands](learn/persist-cli-tool/) for more information. #### Use the generated client API Using the generated client API, you can update the record in the data store with the `put` resource method. ::: code persist_update.bal ::: #### Run the program Execute the command below to run the program. ::: out persist_update.out ::: ================================================ FILE: examples/persist-update/persist_update.metatags ================================================ description: This BBE demonstrates the usage of put resource method in generated persistence client to update the record in the data store. keywords: ballerina, ballerina by example, bbe, persistence, data store, put, select, entity ================================================ FILE: examples/persist-update/persist_update.out ================================================ $ bal run Compiling source foo/rainier:0.1.0 Running executable Deleted employee record: {"id":"16c6553a-373c-4b29-b1c8-c282f444248c","firstName":"John","lastName":"Doe","birthDate":{"year":1987,"month":07,"day":23},"gender":"Male","hireDate":{"year":2014,"month":5,"day":1}} ================================================ FILE: examples/programs-and-modules/programs_and_modules.bal ================================================ // This import declaration binds the prefix `io` to the `ballerina/io` package. // The prefix by default comes from the last part of the package name. // The `ballerina` org name is reserved for the Ballerina library packages. import ballerina/io; // `main` function is the program entry point. // `public` makes the function visible outside the package. public function main() { // Here `io:println` means function `println` is in the package bound to prefix `io`. io:println("Hello, World!"); } ================================================ FILE: examples/programs-and-modules/programs_and_modules.md ================================================ # Programs and modules Every Ballerina program consists of modules. Modules are one or more `.bal` files. The module names take the form `org/x.y.z`. ::: code programs_and_modules.bal ::: ::: out programs_and_modules.out ::: ================================================ FILE: examples/programs-and-modules/programs_and_modules.metatags ================================================ description: This BBE introduces Ballerina modules and how you can import them in your program. keywords: ballerina, ballerina by example, bbe, module, program ================================================ FILE: examples/programs-and-modules/programs_and_modules.out ================================================ $ bal run programs_and_modules.bal Hello, World! ================================================ FILE: examples/provide-function-arguments-by-name/provide_function_arguments_by_name.bal ================================================ import ballerina/io; function add(int x, int y, int z) { io:println("Sum of x, y and z:", x + y + z); } public function main() { // Calls the `add` function using the positional arguments. add(1, 2, 3); // Calls the `add` function using the named arguments in the same order as the parameters of the function definition. add(x = 1, y = 2, z = 3); // Calls the `add` function using the named arguments in a different order from the order of the parameters in the function definition. add(z = 3, y = 2, x = 1); // Calls the `add` function using a combination of named arguments and positional arguments. add(1, z = 3, y = 2); } ================================================ FILE: examples/provide-function-arguments-by-name/provide_function_arguments_by_name.md ================================================ # Provide function arguments by name Ballerina allows you to call functions with named arguments, which do not have to be in the same order as the parameters. ::: code provide_function_arguments_by_name.bal ::: ::: out provide_function_arguments_by_name.out ::: ## Related links - [Functions](/learn/by-example/functions/) - [Included record parameters](/learn/by-example/included-record-parameters/) - [Rest arguments](/learn/by-example/rest-arguments/) ================================================ FILE: examples/provide-function-arguments-by-name/provide_function_arguments_by_name.metatags ================================================ description: This BBE demonstrates calling functions with named arguments and creating functions in Ballerina. keywords: ballerina, ballerina by example, bbe, named arguments, functions, function definition, parameters ================================================ FILE: examples/provide-function-arguments-by-name/provide_function_arguments_by_name.out ================================================ $ bal run provide_function_arguments_by_name.bal Sum of x, y and z:6 Sum of x, y and z:6 Sum of x, y and z:6 Sum of x, y and z:6 ================================================ FILE: examples/providing-services/providing_services.bal ================================================ import ballerina/http; // Listener declarations are allowed at the module level. // They are similar to variable declarations, but register the newly created Listener object with the module. // If `new` returns an error, then module initialization fails. listener http:Listener h = new (8080); ================================================ FILE: examples/providing-services/providing_services.md ================================================ # Provide services Providing services involves the interaction of three main things. 1. Service objects are the counterpart of client objects. Service objects also have remote methods and they are remotely accessible to clients. Remote method of a client object typically calls the remote method of a service object. 2. Listener is the entity that receives the network input and then it makes calls to remote methods of attached service objects. Listeners are registered with the module as illustrated in the following example. 3. Modules have a lifecycle and they are initialized on program startup. Modules start up the registered listeners after the initialization and shut them down during the program shutdown. ::: code providing_services.bal ::: ::: out providing_services.out ::: ================================================ FILE: examples/providing-services/providing_services.metatags ================================================ description: This BBE explains various constructs that are related to proving services. keywords: ballerina, ballerina by example, bbe, Listener, Service, Service Object, Module ================================================ FILE: examples/providing-services/providing_services.out ================================================ $ bal run providing_services.bal ================================================ FILE: examples/query-actions/query_actions.bal ================================================ import ballerina/io; public function main() returns error? { int[] nums = [1, 2, 3, 4]; int[] numsTimes10 = []; // The `from` clause works similar to a `foreach` statement. check from var i in nums // The `do` statement block is evaluated in each iteration. do { numsTimes10.push(i * 10); }; io:println(numsTimes10); // Print only the even numbers in the `nums` array. // Intermediate clauses such as `let` clause, `join` clause, `order by` clause, // `where clause`, and `limit` clause can also be used. check from var i in nums where i % 2 == 0 do { io:println(i); }; } ================================================ FILE: examples/query-actions/query_actions.md ================================================ # Query actions A query action allows you to use the functionalities of a query expression to iteratively execute a code block like a `foreach` statement. A Query action starts with a `from` clause and ends with a `do` clause. It can contain all the intermediate clauses in a query expression. The clauses in a query action are executed in the same way as the clauses in a query expression. If a clause completes early with an error `e`, the result of the query action is `e`. Otherwise, the result is `null`. ::: code query_actions.bal ::: ::: out query_actions.out ::: ## Related links - [Query expressions](/learn/by-example/query-expressions) ================================================ FILE: examples/query-actions/query_actions.metatags ================================================ description: This BBE demonstrates query actions in Ballerina, executing a set of code segments iteratively, a `foreach` statement, using a query expression to execute a set of code segments iteratively. keywords: ballerina, ballerina by example, bbe, query, query action, iterable, foreach, action, do, check ================================================ FILE: examples/query-actions/query_actions.out ================================================ $ bal run query_actions.bal [10,20,30,40] 2 4 ================================================ FILE: examples/query-expressions/query_expressions.bal ================================================ import ballerina/io; public function main() { int[] nums = [1, 2, 3, 4]; // The `from` clause works similar to a `foreach` statement. int[] numsTimes10 = from var i in nums // The `select` clause is evaluated for each iteration. select i * 10; io:println(numsTimes10); // A `where` clause can be used to filter iterable values. // It can occur multiple times anywhere between a `from` and `select` clause. // This will pass the frame to the `select` clause only if `i % 2 == 0` is true. int[] evenNums = from int i in nums where i % 2 == 0 select i; io:println(evenNums); // The `order by` clause can be used to sort the result in // `ascending` or `descending` order. int[] numsReversed = from int i in nums order by i descending select i; io:println(numsReversed); // Iterating a string value using the query expression. string languageName = "Ballerina"; string newName = from var char in languageName select char + char; io:println(newName); } ================================================ FILE: examples/query-expressions/query_expressions.md ================================================ # Query expressions A query expression is similar to the `SQL` query syntax where you can construct a list, a mapping, a table, a stream, or a sequence by iterating over an iterable value. A query expression consists of a sequence of clauses starting with a `from` clause and ending with a `select` clause. ::: code query_expressions.bal ::: ::: out query_expressions.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/query-expressions/query_expressions.metatags ================================================ description: This BBE demonstrates query expressions in Ballerina, creating a list value from a query expression, creating a string value from a query expression, iterating a list, iterating an array, iterating a sequence value, filtering an iterable value, filtering a list, and the `where` clause. keywords: ballerina, ballerina by example, bbe, query, iterate, from, select, language integrated queries, where, condition ================================================ FILE: examples/query-expressions/query_expressions.out ================================================ $ bal run query_expressions.bal [10,20,30,40] [2,4] [4,3,2,1] BBaalllleerriinnaa ================================================ FILE: examples/querying-tables/querying_tables.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: "Jane", lastName: "Smith", salary: 150}, {id: 4, firstName: "Fred", lastName: "Bloggs", salary: 200}, {id: 7, firstName: "Bobby", lastName: "Clark", salary: 300}, {id: 9, firstName: "Cassie", lastName: "Smith", salary: 250} ]; // Since the contextually-expected type for the query expression is `int[]`, // the evaluation of the query expression will result in an integer array. int[] salaries = from var emp in employees select emp.salary; io:println(salaries); // The query expression creates a table based on the contextually-expected type. table highPaidEmployees = from Employee emp in employees where emp.salary > 180 order by emp.firstName select emp; foreach Employee emp in highPaidEmployees { io:println(emp.firstName, " ", emp.lastName); } } ================================================ FILE: examples/querying-tables/querying_tables.md ================================================ # Query tables Tables can be combined with query expressions, unlike maps. The basic type of the output of a query expression is determined by the contextually expected type, and the input type. ::: code querying_tables.bal ::: ::: out querying_tables.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) - [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/querying-tables/querying_tables.metatags ================================================ description: This BBE demonstrates querying tables in Ballerina, filtering tables, ordering tables rows, sorting a table, iterating a table, performing table operations, querying a table, and creating an array from a table. keywords: ballerina, ballerina by example, bbe, querying tables, table, query, query expression, filter, order, sort, iterate, array, where ================================================ FILE: examples/querying-tables/querying_tables.out ================================================ $ bal run querying_tables.bal [100,150,200,300,250] Bobby Clark Cassie Smith Fred Bloggs ================================================ FILE: examples/querying-with-streams/querying_with_streams.bal ================================================ import ballerina/io; class EvenNumberGenerator { int i = 0; public isolated function next() returns record {|int value;|}|error? { self.i += 2; if self.i > 10 { return (); } return {value: self.i}; } } public function main() returns error? { EvenNumberGenerator evenGen = new (); // Creates a `stream` passing an `EvenNumberGenerator` object to the `stream` constructor. stream evenNumberStream = new (evenGen); // Iterates the `evenNumberStream` until it returns `()`. // If the stream terminates with an error, the result of the query expression will be the error. int[] evenNumbers = check from var number in evenNumberStream select number; io:println(evenNumbers); } ================================================ FILE: examples/querying-with-streams/querying_with_streams.md ================================================ # Query streams A query expression can be used to call the `next()` method of a stream iteratively. A binding pattern in a query expression will bind the value of the result from the `next()` operation on the stream. If the stream terminates with an error, result of the query expression will be the error. ::: code querying_with_streams.bal ::: ::: out querying_with_streams.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/querying-with-streams/querying_with_streams.metatags ================================================ description: This BBE demonstrates querying with streams in Ballerina, iterating a stream object, calling the next method of a stream, using a stream object, and handling the termination value of a stream. keywords: ballerina, ballerina by example, bbe, stream, lazy, value, next, query, check ================================================ FILE: examples/querying-with-streams/querying_with_streams.out ================================================ $ bal run querying_with_streams.bal [2,4,6,8,10] ================================================ FILE: examples/rabbitmq-client-basic-auth/rabbitmq_client_basic_auth.bal ================================================ import ballerina/http; import ballerinax/rabbitmq; type Order readonly & record { int orderId; string productName; decimal price; boolean isValid; }; service / on new http:Listener(9092) { private final rabbitmq:Client orderClient; function init() returns error? { // Initiate the RabbitMQ client at the start of the service. This will be used // throughout the lifetime of the service. self.orderClient = check new (rabbitmq:DEFAULT_HOST, rabbitmq:DEFAULT_PORT, // To secure the client connections using username/password authentication, provide the credentials // with the `rabbitmq:Credentials` record. auth = { username: "alice", password: "alice@123" } ); } resource function post orders(Order newOrder) returns http:Accepted|error { // Publishes the message using the `newClient` and the routing key named `OrderQueue`. check self.orderClient->publishMessage({ content: newOrder, routingKey: "OrderQueue" }); return http:ACCEPTED; } } ================================================ FILE: examples/rabbitmq-client-basic-auth/rabbitmq_client_basic_auth.client.out ================================================ $ curl http://localhost:9092/orders -H "Content-type:application/json" -d "{\"orderId\": 1, \"productName\": \"Sport shoe\", \"price\": 27.5, \"isValid\": true}" ================================================ FILE: examples/rabbitmq-client-basic-auth/rabbitmq_client_basic_auth.md ================================================ # RabbitMQ client - Basic authentication The RabbitMQ authentication allows securing the client communication with the server. After an application connects to RabbitMQ and before it can perform operations, it must authenticate (i.e., present and prove its identity). In this example, the underlying connection of the client is secured with Basic Authentication. A secured `rabbitmq:Client` is created by using the default host and port or custom configurations and by providing the authentication details using the `rabbitmq:Credentials` record. Use it to authenticate client connections using a username and password. ::: code rabbitmq_client_basic_auth.bal ::: ## Prerequisites - Start an instance of the [RabbitMQ server](https://www.rabbitmq.com/download.html). Run the client program by executing the following command. ::: out rabbitmq_client_basic_auth.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out rabbitmq_client_basic_auth.client.out ::: ## Related links - [`rabbitmq:Credentials` - API documentation](https://lib.ballerina.io/ballerinax/rabbitmq/latest#Credentials) - [RabbitMQ connection - Specification](https://github.com/ballerina-platform/module-ballerinax-rabbitmq/blob/master/docs/spec/spec.md#2-connection) ================================================ FILE: examples/rabbitmq-client-basic-auth/rabbitmq_client_basic_auth.metatags ================================================ description: This example demonstrates publishing messages to a RabbitMQ queue using a connection with basic authentication. keywords: ballerina, ballerina by example, rabbitmq, ssl, tls, authentication, credentials ================================================ FILE: examples/rabbitmq-client-basic-auth/rabbitmq_client_basic_auth.server.out ================================================ $ bal run rabbitmq-client-basic-auth.bal ================================================ FILE: examples/rabbitmq-client-constraint-validation/rabbitmq_client_constraint_validation.bal ================================================ import ballerina/constraint; import ballerinax/rabbitmq; import ballerina/log; public type Order record { int orderId; // Add a constraint to allow only string values of length between 1 and 30. @constraint:String {maxLength: 30, minLength: 1} string productName; decimal price; boolean isValid; }; public function main() returns error? { // Creates a ballerina RabbitMQ client. rabbitmq:Client newClient = check new (rabbitmq:DEFAULT_HOST, rabbitmq:DEFAULT_PORT); // Consuming message from the routing key OrderQueue. Order|rabbitmq:PayloadValidationError|rabbitmq:Error 'order = newClient->consumePayload("OrderQueue"); if 'order is Order { if 'order.isValid { log:printInfo(string `Received valid order for ${'order.productName}`); } } else if 'order is rabbitmq:PayloadValidationError { log:printError("Payload validation failed", 'order); } else { log:printError("Error occurred while consuming"); } } ================================================ FILE: examples/rabbitmq-client-constraint-validation/rabbitmq_client_constraint_validation.md ================================================ # RabbitMQ client - Constraint validation The Ballerina constraint module allows you to add additional constraints to the message content. The constraints can be added to a given data type using different annotations. When a message with a constraint is received from the RabbitMQ server, it is validated internally. This validation happens soon after the successful data-binding of the message content before returning the message record. If the validation fails, the `rabbitmq:PayloadValidationError` error is returned. Use this to validate the message content as the application receives it, which allows you to guard against unnecessary message content processing and malicious content. ::: code rabbitmq_client_constraint_validation.bal ::: ## Prerequisites - Start an instance of the [RabbitMQ server](https://www.rabbitmq.com/download.html). Run the client program by executing the following command. ::: out rabbitmq_client_constraint_validation.out ::: >**Tip:** You can invoke the above service via the [RabbitMQ client](/learn/by-example/rabbitmq-producer/) example with a valid product name (0 < length <= 30), then with an invalid product name and again with a valid product name. ## Related links - [`rabbitmq:PayloadValidationError` error type - API documentation](https://lib.ballerina.io/ballerinax/rabbitmq/latest#PayloadValidationError) - [`rabbitmq` package - API documentation](https://lib.ballerina.io/ballerinax/rabbitmq/latest) - [`constraint` package - API documentation](https://lib.ballerina.io/ballerina/constraint/latest) ================================================ FILE: examples/rabbitmq-client-constraint-validation/rabbitmq_client_constraint_validation.metatags ================================================ description: This example demonstrates validating a payload according to the constraints defined in the payload record. keywords: ballerina, ballerina by example, bbe, rabbitmq, consumer, listener, service, constraint, validation ================================================ FILE: examples/rabbitmq-client-constraint-validation/rabbitmq_client_constraint_validation.out ================================================ $ bal run rabbitmq_client_constraint_validation.bal time = 2022-12-05T08:18:09.346+05:30 level = INFO module = "" message = "Received valid order for Sport shoe" time = 2022-12-05T08:18:49.844+05:30 level = ERROR module = "" message = "Payload validation failed" error = "Validation failed for \'$.productName:maxLength\' constraint(s)." time = 2022-12-05T08:19:19.214+05:30 level = INFO module = "" message = "Received valid order for Sport shoe" ================================================ FILE: examples/rabbitmq-client-secure-connection/rabbitmq_client_secure_connection.bal ================================================ import ballerina/http; import ballerinax/rabbitmq; type Order readonly & record { int orderId; string productName; decimal price; boolean isValid; }; service / on new http:Listener(9092) { private final rabbitmq:Client orderClient; function init() returns error? { // Initiate the RabbitMQ client at the start of the service. This will be used // throughout the lifetime of the service. self.orderClient = check new (rabbitmq:DEFAULT_HOST, 5671, // To secure the client connection using TLS/SSL, the client needs to be configured with // a certificate file of the server. secureSocket = { cert: "../resource/path/to/public.crt" } ); } resource function post orders(Order newOrder) returns http:Accepted|error { // Publishes the message using the `newClient` and the routing key named `OrderQueue`. check self.orderClient->publishMessage({ content: newOrder, routingKey: "OrderQueue" }); return http:ACCEPTED; } } ================================================ FILE: examples/rabbitmq-client-secure-connection/rabbitmq_client_secure_connection.client.out ================================================ $ curl http://localhost:9092/orders -H "Content-type:application/json" -d "{\"orderId\": 1, \"productName\": \"Sport shoe\", \"price\": 27.5, \"isValid\": true}" ================================================ FILE: examples/rabbitmq-client-secure-connection/rabbitmq_client_secure_connection.md ================================================ # RabbitMQ client - SSL/TLS The `rabbitmq:Client` can be configured to connect to the server via SSL/TLS by providing a certificate file. The certificate can be provided through the `secureSocket` field of the `rabbitmq:ConnectionConfiguration`. Use this to secure the communication between the client and the server. ::: code rabbitmq_client_secure_connection.bal ::: ## Prerequisites - Start an instance of the [RabbitMQ server](https://www.rabbitmq.com/download.html). Run the client program by executing the following command. ::: out rabbitmq_client_secure_connection.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out rabbitmq_client_secure_connection.client.out ::: ## Related links - [`rabbitmq:SecureSocket` - API documentation](https://lib.ballerina.io/ballerinax/rabbitmq/latest#SecureSocket) - [RabbitMQ connection - Specification](https://github.com/ballerina-platform/module-ballerinax-rabbitmq/blob/master/docs/spec/spec.md#2-connection) ================================================ FILE: examples/rabbitmq-client-secure-connection/rabbitmq_client_secure_connection.metatags ================================================ description: This example demonstrates consuming messages from a RabbitMQ queue using a secured connection. keywords: ballerina, ballerina by example, rabbitmq, ssl, tls, authentication, credentials ================================================ FILE: examples/rabbitmq-client-secure-connection/rabbitmq_client_secure_connection.server.out ================================================ $ bal run rabbitmq-client-secure-connection.bal ================================================ FILE: examples/rabbitmq-consumer/rabbitmq_consumer.bal ================================================ import ballerina/log; import ballerinax/rabbitmq; public type Order record { int orderId; string productName; decimal price; boolean isValid; }; // The consumer service listens to the "OrderQueue" queue. service "OrderQueue" on new rabbitmq:Listener(rabbitmq:DEFAULT_HOST, rabbitmq:DEFAULT_PORT) { remote function onMessage(Order 'order) returns error? { if 'order.isValid { log:printInfo(string `Received valid order for ${'order.productName}`); } } } ================================================ FILE: examples/rabbitmq-consumer/rabbitmq_consumer.md ================================================ # RabbitMQ service - Consume message The `rabbitmq:Service` listens to the given queue for incoming messages. When a publisher sends a message on a queue, any active service listening on that queue receives the message. A `rabbitmq:Listener` is created by passing the host and port of the RabbiMQ broker. A `rabbitmq:Service` attached to the listener is used to listen to a specific queue and consume incoming messages. The queue to listen to should be given as the service name or in the `queueName` field of the `rabbitmq:ServiceConfig`. Use it to listen to messages sent to a particular queue. ::: code rabbitmq_consumer.bal ::: ## Prerequisites - Start an instance of the [RabbitMQ server](https://www.rabbitmq.com/download.html). Run the service by executing the following command. ::: out rabbitmq_consumer.out ::: >**Tip:** You can invoke the above service via the [RabbitMQ client](/learn/by-example/rabbitmq-producer/). ## Related links - [`rabbitmq` package - API documentation](https://lib.ballerina.io/ballerinax/rabbitmq/latest) - [RabbitMQ subscribing - Specification](https://github.com/ballerina-platform/module-ballerinax-rabbitmq/blob/master/docs/spec/spec.md#6-subscribing) ================================================ FILE: examples/rabbitmq-consumer/rabbitmq_consumer.metatags ================================================ description: This example demonstrates consuming messages from a RabbitMQ queue in an asynchronous manner. keywords: ballerina, ballerina by example, rabbitmq, consumer, bbe, service, asynchronous ================================================ FILE: examples/rabbitmq-consumer/rabbitmq_consumer.out ================================================ $ bal run rabbitmq_consumer.bal time = 2021-05-20T14:49:11.011+05:30 level = INFO module = "" message = "Received message: Hello from Ballerina" ================================================ FILE: examples/rabbitmq-consumer-with-client-acknowledgement/rabbitmq_consumer_with_client_acknowledgement.bal ================================================ import ballerina/log; import ballerinax/rabbitmq; public type StringMessage record {| *rabbitmq:AnydataMessage; string content; |}; // The consumer service listens to the "OrderQueue" queue. // The `ackMode` is by default rabbitmq:AUTO_ACK where messages are acknowledged // immediately after consuming. @rabbitmq:ServiceConfig { queueName: "OrderQueue", autoAck: false } service rabbitmq:Service on new rabbitmq:Listener(rabbitmq:DEFAULT_HOST, rabbitmq:DEFAULT_PORT) { remote function onMessage(StringMessage message, rabbitmq:Caller caller) returns error? { log:printInfo("Received message: " + message.content); // Positively acknowledges a single message. check caller->basicAck(); } } ================================================ FILE: examples/rabbitmq-consumer-with-client-acknowledgement/rabbitmq_consumer_with_client_acknowledgement.md ================================================ # RabbitMQ service - Consumer with acknowledgement The `rabbitmq:Caller` allows manual acknowledgments for the consumed messages. A `rabbitmq:Listener` is created by passing the host and port of the RabbiMQ broker. A `rabbitmq:Service` attached to the `rabbitmq:Listener` can be used to listen to a specific subject and consume incoming messages. To enable manual acknowledgments, set the `autoAck` mode in `rabbitmq:ServiceConfig` to `false`. The `rabbitmq:Caller` can be used to acknowledge the message positively or negatively using the `basicAck` and `basicNack` functions. Use it to manually acknowledge the consumed messages. ::: code rabbitmq_consumer_with_client_acknowledgement.bal ::: ## Prerequisites - Start an instance of the [RabbitMQ server](https://www.rabbitmq.com/download.html). Run the service by executing the following command. ::: out rabbitmq_consumer_with_client_acknowledgement.out ::: >**Tip:** You can invoke the above service via the [RabbitMQ client](/learn/by-example/rabbitmq-producer/). ## Related links - [`rabbitmq:Caller` client object - API documentation](https://lib.ballerina.io/ballerinax/rabbitmq/latest#Caller) - [RabbitMQ client acknowledgements - Specification](https://github.com/ballerina-platform/module-ballerinax-rabbitmq/blob/master/docs/spec/spec.md#8-client-acknowledgements) ================================================ FILE: examples/rabbitmq-consumer-with-client-acknowledgement/rabbitmq_consumer_with_client_acknowledgement.metatags ================================================ description: This example demonstrates manually acknowledging the messages consumed from a RabbitMQ queue. keywords: ballerina, ballerina by example, rabbitmq, consumer, bbe, acknowledgment ================================================ FILE: examples/rabbitmq-consumer-with-client-acknowledgement/rabbitmq_consumer_with_client_acknowledgement.out ================================================ $ bal run rabbitmq_consumer_with_client_acknowledgement.bal time = 2021-05-20T14:53:56.067+05:30 level = INFO module = "" message = "Received message: Hello from Ballerina" ================================================ FILE: examples/rabbitmq-producer/rabbitmq_producer.bal ================================================ import ballerina/http; import ballerinax/rabbitmq; type Order readonly & record { int orderId; string productName; decimal price; boolean isValid; }; service / on new http:Listener(9092) { private final rabbitmq:Client orderClient; function init() returns error? { // Initiate the RabbitMQ client at the start of the service. This will be used // throughout the lifetime of the service. self.orderClient = check new (rabbitmq:DEFAULT_HOST, rabbitmq:DEFAULT_PORT); } resource function post orders(Order newOrder) returns http:Accepted|error { // Publishes the message using the `newClient` and the routing key named `OrderQueue`. check self.orderClient->publishMessage({ content: newOrder, routingKey: "OrderQueue" }); return http:ACCEPTED; } } ================================================ FILE: examples/rabbitmq-producer/rabbitmq_producer.client.out ================================================ $ curl http://localhost:9092/orders -H "Content-type:application/json" -d "{\"orderId\": 1, \"productName\": \"Sport shoe\", \"price\": 27.5, \"isValid\": true}" ================================================ FILE: examples/rabbitmq-producer/rabbitmq_producer.md ================================================ # RabbitMQ client - Produce message The `rabbitmq:Client` allows sending messages to a given pre-declared queue. A `rabbitmq:Client` is created by passing the host and port of the RabbitMQ broker. For more details on declaring the queue, see the `RabbitMQ client - Declare a queue` sample. The `publishMessage` method, which requires the queue name as the routing key and the message content is used to publish messages. Use it to publish messages that can be received by one or more consumers. ::: code rabbitmq_producer.bal ::: ## Prerequisites - Start an instance of the [RabbitMQ server](https://www.rabbitmq.com/download.html). - Declare the queue as given in the [RabbitMQ client - Declare queue](/learn/by-example/rabbitmq-queue-declare/) example. - Run the RabbitMQ service given in the [RabbitMQ service - Consume message](/learn/by-example/rabbitmq-consumer/) example. Run the client program by executing the following command. ::: out rabbitmq_producer.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out rabbitmq_producer.client.out ::: ## Related links - [`rabbitmq:Client` client object - API documentation](https://lib.ballerina.io/ballerinax/rabbitmq/latest#Client) - [RabbitMQ publishing - Specification](https://github.com/ballerina-platform/module-ballerinax-rabbitmq/blob/master/docs/spec/spec.md#5-publishing) ================================================ FILE: examples/rabbitmq-producer/rabbitmq_producer.metatags ================================================ description: This example demonstrates producing messages to a RabbitMQ queue. keywords: ballerina, ballerina by example, rabbitmq, producer, bbe, client ================================================ FILE: examples/rabbitmq-producer/rabbitmq_producer.server.out ================================================ $ bal run rabbitmq_producer.bal ================================================ FILE: examples/rabbitmq-queue-declare/rabbitmq_queue_declare.bal ================================================ import ballerinax/rabbitmq; public function main() returns error? { // Creates a ballerina RabbitMQ client. rabbitmq:Client newClient = check new (rabbitmq:DEFAULT_HOST, rabbitmq:DEFAULT_PORT); // Declares the queue, OrderQueue. check newClient->queueDeclare("OrderQueue"); } ================================================ FILE: examples/rabbitmq-queue-declare/rabbitmq_queue_declare.md ================================================ # RabbitMQ client - Declare queue A RabbitMQ queue is a buffer that stores messages. The queue takes messages from the publisher and sends them to the consumer. A `rabbitmq:Client` is created with the host and port of the RabbitMQ broker. A queue with a specific name and custom configurations can be declared by using the `queueDeclare` method. To create a queue with a server-generated name, the `queueAutoGenerate` method can be used. Use this to declare the queue before publishing messages. ::: code rabbitmq_queue_declare.bal ::: ## Prerequisites - Start an instance of the [RabbitMQ server](https://www.rabbitmq.com/download.html). Run the client program by executing the following command. ::: out rabbitmq_queue_declare.out ::: ## Related links - [`rabbitmq:Client` client object - API documentation](https://lib.ballerina.io/ballerinax/rabbitmq/latest#Client) - [RabbitMQ exchanges and queues - Specification](https://github.com/ballerina-platform/module-ballerinax-rabbitmq/blob/master/docs/spec/spec.md#3-exchanges-and-queues) ================================================ FILE: examples/rabbitmq-queue-declare/rabbitmq_queue_declare.metatags ================================================ description: This example demonstrates declaring a RabbitMQ queue. keywords: ballerina, ballerina by example, rabbitmq, queue, bbe, client ================================================ FILE: examples/rabbitmq-queue-declare/rabbitmq_queue_declare.out ================================================ $ bal run rabbitmq_queue_declare.bal ================================================ FILE: examples/rabbitmq-service-basic-auth/rabbitmq_service_basic_auth.bal ================================================ import ballerina/log; import ballerinax/rabbitmq; public type Order record { int orderId; string productName; decimal price; boolean isValid; }; listener rabbitmq:Listener orderListener = new (rabbitmq:DEFAULT_HOST, 5671, // Provide the credentials to secure the listener connections using username/password authentication. // with the `rabbitmq:Credentials` record. auth = { username: "alice", password: "alice@123" } ); // The consumer service listens to the `OrderQueue` queue. service "OrderQueue" on orderListener { remote function onMessage(Order 'order) returns error? { if 'order.isValid { log:printInfo(string `Received valid order for ${'order.productName}`); } } } ================================================ FILE: examples/rabbitmq-service-basic-auth/rabbitmq_service_basic_auth.md ================================================ # RabbitMQ service - Basic authentication The RabbitMQ authentication allows securing the client communication with the server. After an application connects to RabbitMQ and before it can perform operations, it must authenticate (i.e., present and prove its identity). In this example, the underlying connection of the listener is secured with Basic Authentication. A secured `rabbitmq:Listener` is created by using the default host and port or custom configurations and by providing the authentication details using the `rabbitmq:Credentials` record. Use it to authenticate client connections using a username and password. ::: code rabbitmq_service_basic_auth.bal ::: ## Prerequisites - Start an instance of the [RabbitMQ server](https://www.rabbitmq.com/download.html). Run the service by executing the following command. ::: out rabbitmq_service_basic_auth.out ::: >**Tip:** You can invoke the above service via the [RabbitMQ client - Basic authentication](/learn/by-example/rabbitmq-client-basic-auth/). ## Related links - [`rabbitmq:Credentials` - API documentation](https://lib.ballerina.io/ballerinax/rabbitmq/latest#Credentials) - [RabbitMQ connection - Specification](https://github.com/ballerina-platform/module-ballerinax-rabbitmq/blob/master/docs/spec/spec.md#2-connection) ================================================ FILE: examples/rabbitmq-service-basic-auth/rabbitmq_service_basic_auth.metatags ================================================ description: This example demonstrates consuming messages from a RabbitMQ queue using a connection with basic authentication. keywords: ballerina, ballerina by example, rabbitmq, ssl, tls, authentication, credentials ================================================ FILE: examples/rabbitmq-service-basic-auth/rabbitmq_service_basic_auth.out ================================================ $ bal run rabbitmq_service_basic_auth.bal time = 2021-05-20T14:49:11.011+05:30 level = INFO module = "" message = "Received message: Hello from Ballerina" ================================================ FILE: examples/rabbitmq-service-constraint-validation/rabbitmq_service_constraint_validation.bal ================================================ import ballerina/constraint; import ballerina/log; import ballerinax/rabbitmq; public type Order record { int orderId; // Add a constraint to allow only string values of length between 1 and 30. @constraint:String {maxLength: 30, minLength: 1} string productName; decimal price; boolean isValid; }; // The consumer service listens to the "OrderQueue" queue. service "OrderQueue" on new rabbitmq:Listener(rabbitmq:DEFAULT_HOST, rabbitmq:DEFAULT_PORT) { remote function onMessage(Order 'order) returns error? { if 'order.isValid { log:printInfo(string `Received valid order for ${'order.productName}`); } } // When an error occurs, `onError` gets invoked. remote function onError(rabbitmq:AnydataMessage message, rabbitmq:Error err) { if err is rabbitmq:PayloadValidationError { log:printError("Payload validation failed", err); } } } ================================================ FILE: examples/rabbitmq-service-constraint-validation/rabbitmq_service_constraint_validation.md ================================================ # RabbitMQ service - Constraint validation The Ballerina constraint module allows you to add additional constraints to the message content. The constraints can be added to a given data type using different annotations. When a message with a constraint is received from the RabbitMQ server, it is validated internally. This validation happens soon after the successful data-binding of the message content before executing the `onMessage` remote method. If the validation fails, the `onError` remote method is invoked with the error type `nats:PayloadValidationError`. Use this to validate the message content as the application receives it, which allows you to guard against unnecessary remote method processing and malicious content. ::: code rabbitmq_service_constraint_validation.bal ::: ## Prerequisites - Start an instance of the [RabbitMQ server](https://www.rabbitmq.com/download.html). Run the service by executing the following command. ::: out rabbitmq_service_constraint_validation.out ::: >**Tip:** You can invoke the above service via the [RabbitMQ client](/learn/by-example/rabbitmq-producer/) example with a valid product name (0 < length <= 30), then with an invalid product name and again with a valid product name. ## Related links - [`rabbitmq:PayloadValidationError` error type - API documentation](https://lib.ballerina.io/ballerinax/rabbitmq/latest#PayloadValidationError) - [`rabbitmq` package - API documentation](https://lib.ballerina.io/ballerinax/rabbitmq/latest) - [`constraint` package - API documentation](https://lib.ballerina.io/ballerina/constraint/latest) ================================================ FILE: examples/rabbitmq-service-constraint-validation/rabbitmq_service_constraint_validation.metatags ================================================ description: This example demonstrates validating a payload according to the constraints defined in the payload record. keywords: ballerina, ballerina by example, bbe, rabbitmq, consumer, listener, service, constraint, validation ================================================ FILE: examples/rabbitmq-service-constraint-validation/rabbitmq_service_constraint_validation.out ================================================ $ bal run rabbitmq_service_constraint_validation.bal time = 2022-12-05T08:08:36.426+05:30 level = INFO module = "" message = "Received valid order for Sport shoe" time = 2022-12-05T08:08:57.313+05:30 level = ERROR module = "" message = "Payload validation failed" error = "Validation failed for \'$.productName:maxLength\' constraint(s)." time = 2022-12-05T08:09:19.634+05:30 level = INFO module = "" message = "Received valid order for Sport shoe" ================================================ FILE: examples/rabbitmq-service-secure-connection/rabbitmq_service_secure_connection.bal ================================================ import ballerina/log; import ballerinax/rabbitmq; public type Order record { int orderId; string productName; decimal price; boolean isValid; }; listener rabbitmq:Listener orderListener = new (rabbitmq:DEFAULT_HOST, 5671, // To secure the client connection using TLS/SSL, the client needs to be configured with // a certificate file of the server. secureSocket = { cert: "../resource/path/to/public.crt" } ); // The consumer service listens to the `OrderQueue` queue. service "OrderQueue" on orderListener { remote function onMessage(Order 'order) returns error? { if 'order.isValid { log:printInfo(string `Received valid order for ${'order.productName}`); } } } ================================================ FILE: examples/rabbitmq-service-secure-connection/rabbitmq_service_secure_connection.md ================================================ # RabbitMQ service - SSL/TLS The `rabbitmq:Listener` can be configured to connect to the server via SSL/TLS by providing a certificate file. The certificate can be provided through the `secureSocket` field of the connection configuration. Use this to secure the communication between the client and the server. ::: code rabbitmq_service_secure_connection.bal ::: ## Prerequisites - Start an instance of the [RabbitMQ server](https://www.rabbitmq.com/download.html). Run the service by executing the following command. ::: out rabbitmq_service_secure_connection.out ::: >**Tip:** You can invoke the above service via the [RabbitMQ client - SSL/TLS](/learn/by-example/rabbitmq-client-secure-connection/). ## Related links - [`rabbitmq:SecureSocket` - API documentation](https://lib.ballerina.io/ballerinax/rabbitmq/latest#SecureSocket) - [RabbitMQ connection - Specification](https://github.com/ballerina-platform/module-ballerinax-rabbitmq/blob/master/docs/spec/spec.md#2-connection) ================================================ FILE: examples/rabbitmq-service-secure-connection/rabbitmq_service_secure_connection.metatags ================================================ description: This example demonstrates consuming messages from a RabbitMQ queue using a secured connection. keywords: ballerina, ballerina by example, rabbitmq, ssl, tls, authentication, credentials ================================================ FILE: examples/rabbitmq-service-secure-connection/rabbitmq_service_secure_connection.out ================================================ $ bal run rabbitmq_service_secure_connection.bal time = 2021-05-20T14:49:11.011+05:30 level = INFO module = "" message = "Received message: Hello from Ballerina" ================================================ FILE: examples/rabbitmq-sync-consumer/rabbitmq_sync_consumer.bal ================================================ import ballerina/log; import ballerinax/rabbitmq; public type Order record { int orderId; string productName; decimal price; boolean isValid; }; public function main() returns error? { // Creates a ballerina RabbitMQ client. rabbitmq:Client newClient = check new (rabbitmq:DEFAULT_HOST, rabbitmq:DEFAULT_PORT); // Consuming message from the routing key OrderQueue. Order 'order = check newClient->consumePayload("OrderQueue"); if 'order.isValid { log:printInfo(string `Received valid order for ${'order.productName}`); } } ================================================ FILE: examples/rabbitmq-sync-consumer/rabbitmq_sync_consumer.md ================================================ # RabbitMQ client - Consume message The `rabbitmq:Client` allows fetching individual messages one by one from the server. A `rabbitmq:Client` is created by passing the host and port of the RabbitMQ broker. To pull messages, use the `consumePayload` or `consumeMessage` method, which requires the queue name as the argument. Messages are fetched in the FIFO order. It is possible to use automatic or manual acknowledgments similar to consumer services. Use it to pull messages one by one from a queue in the RabbitMQ server. ::: code rabbitmq_sync_consumer.bal ::: ## Prerequisites - Start an instance of the [RabbitMQ server](https://www.rabbitmq.com/download.html). Run the client program by executing the following command. ::: out rabbitmq_sync_consumer.out ::: >**Tip:** You can try out the above consumer via the [RabbitMQ client](/learn/by-example/rabbitmq-producer/). ## Related links - [`rabbitmq:Client` client object - API documentation](https://lib.ballerina.io/ballerinax/rabbitmq/latest#Client) - [RabbitMQ pull API - Specification](https://github.com/ballerina-platform/module-ballerinax-rabbitmq/blob/master/docs/spec/spec.md#7-retrieving-individual-messages) ================================================ FILE: examples/rabbitmq-sync-consumer/rabbitmq_sync_consumer.metatags ================================================ description: This example demonstrates consuming messages from a RabbitMQ queue synchronously. keywords: ballerina, ballerina by example, rabbitmq, pull, consumer, client, bbe ================================================ FILE: examples/rabbitmq-sync-consumer/rabbitmq_sync_consumer.out ================================================ $ bal run rabbitmq_sync_consumer.bal time = 2021-05-20T14:49:11.011+05:30 level = INFO module = "" message = "Received message: Hello from Ballerina" ================================================ FILE: examples/rabbitmq-transaction-consumer/rabbitmq_transaction_consumer.bal ================================================ import ballerina/log; import ballerinax/rabbitmq; public type StringMessage record {| *rabbitmq:AnydataMessage; string content; |}; // The consumer service listens to the "MyQueue" queue. @rabbitmq:ServiceConfig { queueName: "OrderQueue", autoAck: false } service on new rabbitmq:Listener(rabbitmq:DEFAULT_HOST, rabbitmq:DEFAULT_PORT) { remote function onMessage(StringMessage message, rabbitmq:Caller caller) returns error? { // Acknowledges a single message positively. transaction { log:printInfo("Received message: " + message.content); // Positively acknowledges a single message. check caller->basicAck(); check commit; } } } ================================================ FILE: examples/rabbitmq-transaction-consumer/rabbitmq_transaction_consumer.md ================================================ # RabbitMQ service - Transactional consumer The `rabbitmq:Service` can become a transactional consumer by acknowledging messages within a Ballerina transaction block. A `rabbitmq:Listener` can be created by passing the host and port of the RabbitMQ broker. A `rabbitmq:Service` attached to the listener can be used to listen to a specific queue. The queue to listen to should be given as the service name or in the `queueName` field of the `rabbitmq:ServiceConfig`. The `rabbitmq:Caller`, which is passed as an argument in the `onMessage` remote method is used to acknowledge the message inside a transaction block. Use it to consume messages with ensured acknowledgment to the RabbitMQ server. ::: code rabbitmq_transaction_consumer.bal ::: ## Prerequisites - Start an instance of the [RabbitMQ server](https://www.rabbitmq.com/download.html). Run the service by executing the following command. ::: out rabbitmq_transaction_consumer.out ::: >**Tip:** You can invoke the above service via the [RabbitMQ client - Transactional producer](/learn/by-example/rabbitmq-transaction-producer//). ## Related links - [`rabbitmq` package - API documentation](https://lib.ballerina.io/ballerinax/rabbitmq/latest) - [RabbitMQ client acknowledgments - Specification](https://github.com/ballerina-platform/module-ballerinax-rabbitmq/blob/master/docs/spec/spec.md#8-client-acknowledgements) ================================================ FILE: examples/rabbitmq-transaction-consumer/rabbitmq_transaction_consumer.metatags ================================================ description: This example demonstrates transactionally acknowledging messages consumed from a RabbitMQ queue. keywords: ballerina, ballerina by example, rabbitmq, bbe, consumer, transaction ================================================ FILE: examples/rabbitmq-transaction-consumer/rabbitmq_transaction_consumer.out ================================================ $ bal run rabbitmq_transaction_consumer.bal time = 2021-01-18 15:15:36,514 level = INFO module = "" message = "The message received: Hello from Ballerina" ================================================ FILE: examples/rabbitmq-transaction-producer/rabbitmq_transaction_producer.bal ================================================ import ballerina/http; import ballerinax/rabbitmq; type Order readonly & record { int orderId; string productName; decimal price; boolean isValid; }; service / on new http:Listener(9092) { private final rabbitmq:Client orderClient; function init() returns error? { // Initiate the RabbitMQ client at the start of the service. This will be used // throughout the lifetime of the service. self.orderClient = check new (rabbitmq:DEFAULT_HOST, rabbitmq:DEFAULT_PORT); } resource function post orders(Order newOrder) returns http:Accepted|error { transaction { // Publishes the message using the `newClient` and the routing key named `OrderQueue`. check self.orderClient->publishMessage({ content: newOrder, routingKey: "OrderQueue" }); check commit; } return http:ACCEPTED; } } ================================================ FILE: examples/rabbitmq-transaction-producer/rabbitmq_transaction_producer.md ================================================ # RabbitMQ client - Transactional producer The `rabbitmq:Client` can become a transactional producer by publishing messages within a Ballerina transaction block. Upon successful execution of the transaction block, the client will commit or roll back in the case of any error. A `rabbitmq:Client` can be created by passing the host and port of the RabbitMQ broker. To publish messages, the `publishMessage` method, which requires the message and queue name as arguments is used. Use it to publish messages to the RabbitMQ server with ensured delivery. ::: code rabbitmq_transaction_producer.bal ::: ## Prerequisites - Start an instance of the [RabbitMQ server](https://www.rabbitmq.com/download.html). - Declare the queue as given in the [RabbitMQ client - Declare queue](/learn/by-example/rabbitmq-queue-declare/) example. - Run the RabbitMQ service given in the [RabbitMQ service - Transactional consumer](/learn/by-example/rabbitmq-transaction-consumer/) example. Run the client program by executing the following command. ::: out rabbitmq_transaction_producer.out ::: ## Related links - [`rabbitmq:Client` client object - API documentation](https://lib.ballerina.io/ballerinax/rabbitmq/latest#Client) - [RabbitMQ publishing - Specification](https://github.com/ballerina-platform/module-ballerinax-rabbitmq/blob/master/docs/spec/spec.md#5-publishing) ================================================ FILE: examples/rabbitmq-transaction-producer/rabbitmq_transaction_producer.metatags ================================================ description: This example demonstrates transactionally producing messages to a RabbitMQ queue. keywords: ballerina, ballerina by example, rabbitmq, producer, bbe, transaction ================================================ FILE: examples/rabbitmq-transaction-producer/rabbitmq_transaction_producer.out ================================================ $ bal run rabbitmq_transaction_producer.bal ================================================ FILE: examples/rag-ingestion-with-external-vector-store/rag_ingestion_with_external_vector_store.bal ================================================ import ballerina/ai; import ballerina/io; import ballerinax/ai.pinecone; // Configuration for Pinecone. configurable string pineconeServiceUrl = ?; configurable string pineconeApiKey = ?; // Define the vector store to use. // The example uses Pinecone. Alternatively, you can use other providers // or try out the in-memory vector store (`ai:InMemoryVectorStore`). final ai:VectorStore vectorStore = check new pinecone:VectorStore(pineconeServiceUrl, pineconeApiKey); // Define the embedding provider to use. // The example uses the default embedding provider implementation // (with configuration added via a Ballerina VS Code command). final ai:EmbeddingProvider embeddingProvider = check ai:getDefaultEmbeddingProvider(); // Create the knowledge base with the vector store and embedding provider. final ai:KnowledgeBase knowledgeBase = new ai:VectorKnowledgeBase(vectorStore, embeddingProvider); public function main() returns error? { // Use data loaders to load documents. ai:DataLoader loader = check new ai:TextDataLoader("./leave_policy.pdf"); ai:Document|ai:Document[] documents = check loader.load(); // Ingest the documents into the knowledge base. // When `ai:Document`s are ingested, chunking is handled by the knowledge base. // Alternatively, for finer control, you can use the required chunker // (`ai:Chunker` implementations) to chunk documents and pass the chunks // as the argument. ai:Error? ingestionResult = knowledgeBase.ingest(documents); if ingestionResult is () { io:println("Ingestion successful"); } else { io:println("Ingestion failed: ", ingestionResult.message()); } } ================================================ FILE: examples/rag-ingestion-with-external-vector-store/rag_ingestion_with_external_vector_store.md ================================================ # Retrieval-augmented generation (RAG) ingestion Ballerina has high-level, provider-agnostic APIs to ingest data for retrieval-augmented generation (RAG) workflows. These include abstractions such as `ai:DataLoader`, `ai:VectorStore`, `ai:EmbeddingProvider`, and `ai:KnowledgeBase`. These abstractions enable you to load documents, convert them into semantically meaningful vector representations using embedding models, and index them into a vector database (e.g., Pinecone, Weaviate, etc.). The knowledge base (`ai: KnowledgeBase`) orchestrates this process. This example demonstrates how to use `ai:TextDataLoader` to load documents, generate embeddings with a configured provider, and ingest them into a vector store. > Note: This example uses the default embedding provider implementation and Pinecone. To generate the configuration for the embedding provider, 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.` embedding provider implementation. Follow [`ballerinax/ai.pinecone` prerequisites](https://central.ballerina.io/ballerinax/ai.pinecone/latest#prerequisites) to extract Pinecone configuration. Alternatively, you can try out the in-memory vector store (`ai:InMemoryVectorStore`). For more information on the underlying module, see the [`ballerina/ai` module](https://lib.ballerina.io/ballerina/ai/latest/). ::: code rag_ingestion_with_external_vector_store.bal ::: ::: out rag_ingestion_with_external_vector_store.out ::: ## Related links - [Sample policy document](https://github.com/ballerina-platform/ballerina-distribution/tree/master/examples/rag-ingestion-with-external-vector-store/leave_policy.pdf) - [RAG query with external vector store example](/learn/by-example/rag-query-with-external-vector-store/) - [The `ballerinax/ai.milvus` module](https://central.ballerina.io/ballerinax/ai.milvus/latest) - [The `ballerinax/ai.pinecone` module](https://central.ballerina.io/ballerinax/ai.pinecone/latest) - [The `ballerinax/ai.pgvector` module](https://central.ballerina.io/ballerinax/ai.pgvector/latest) - [The `ballerinax/ai.weaviate` module](https://central.ballerina.io/ballerinax/ai.weaviate/latest) ================================================ FILE: examples/rag-ingestion-with-external-vector-store/rag_ingestion_with_external_vector_store.metatags ================================================ description: This BBE demonstrates how to do data ingestion for retrieval-augmented generation (RAG). keywords: ballerina, ballerina by example, BBE, ai, llm, rag ================================================ FILE: examples/rag-ingestion-with-external-vector-store/rag_ingestion_with_external_vector_store.out ================================================ $ bal run rag_ingestion_with_external_vector_store.bal Ingestion successful ================================================ FILE: examples/rag-query-with-external-vector-store/rag_query_with_external_vector_store.bal ================================================ import ballerina/ai; import ballerina/io; import ballerinax/ai.pinecone; // Configuration for Pinecone. configurable string pineconeServiceUrl = ?; configurable string pineconeApiKey = ?; // Define the vector store to use. // The example uses Pinecone. Alternatively, you can use other providers // or try out the in-memory vector store (`ai:InMemoryVectorStore`). final ai:VectorStore vectorStore = check new pinecone:VectorStore(pineconeServiceUrl, pineconeApiKey); // Define the embedding provider to use. // The example uses the default embedding provider implementation // (with configuration added via a Ballerina VS Code command). final ai:EmbeddingProvider embeddingProvider = check ai:getDefaultEmbeddingProvider(); // Create the knowledge base with the vector store and embedding provider. final ai:KnowledgeBase knowledgeBase = new ai:VectorKnowledgeBase(vectorStore, embeddingProvider); // Define the model provider to use. // The example uses the default model provider implementation // (with configuration added via a Ballerina VS Code command). final ai:ModelProvider modelProvider = check ai:getDefaultModelProvider(); public function main() returns error? { string appealQuery = "What is the process for appealing a rejected leave request?"; // Retrieve the relevant context (chunks) from the knowledge base. ai:QueryMatch[] queryMatches = check knowledgeBase.retrieve(appealQuery, 10); ai:Chunk[] context = from ai:QueryMatch queryMatch in queryMatches select queryMatch.chunk; // Use the `generate` method, inserting the context and query to the prompt. string answer = check modelProvider->generate(`Answer the query based on the following context: Context: ${context} Query: ${appealQuery} Base the answer only on the above context. If the answer is not contained within the context, respond with "I don't know".`); io:println("Query: ", appealQuery); io:println("Answer: ", answer); string carryForwardQuery = "How many annual leave days can a full-time employee carry forward to the next year?"; queryMatches = check knowledgeBase.retrieve(carryForwardQuery, 10); context = from ai:QueryMatch queryMatch in queryMatches select queryMatch.chunk; // The `augmentUserQuery` function augments the user query with the context using // a generic prompt template. ai:ChatUserMessage augmentedQuery = ai:augmentUserQuery(context, carryForwardQuery); // Use the `chat` method with the `ai:ChatUserMessage` with the augmented query. ai:ChatAssistantMessage assistantMessage = check modelProvider->chat(augmentedQuery); io:println("\nQuery: ", carryForwardQuery); io:println("Answer: ", assistantMessage.content); } ================================================ FILE: examples/rag-query-with-external-vector-store/rag_query_with_external_vector_store.md ================================================ # Retrieval-augmented generation (RAG) query Retrieval-augmented generation (RAG) is a technique that enhances capabilities of large language models by combining them with external knowledge sources to provide more accurate and contextually-relevant responses. Ballerina has high-level, provider-agnostic APIs for retrieval-augmented generation (RAG) workflows. These include abstractions such as `ai:VectorStore`, `ai:EmbeddingProvider`, and `ai:KnowledgeBase`. These abstractions enable you to query semantically similar content from vector databases (e.g., Pinecone, Weaviate, etc.) and use retrieved context in the request to the LLM to generate more accurate responses. This example demonstrates how to query a knowledge base to retrieve relevant documents and use them with a language model to answer questions based on the retrieved context. > Note: You can follow the [RAG ingestion](/learn/by-example/rag-ingestion/) example to ingest data first. > Note: This example uses the default model provider and embedding provider implementations and Pinecone. To generate the configuration for the model and embedding providers, 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.` embedding provider implementation. Follow [`ballerinax/ai.pinecone` prerequisites](https://central.ballerina.io/ballerinax/ai.pinecone/latest#prerequisites) to extract Pinecone configuration. Alternatively, you can try out the in-memory vector store (`ai:InMemoryVectorStore`). For more information on the underlying module, see the [`ballerina/ai` module](https://lib.ballerina.io/ballerina/ai/latest/). ::: code rag_query_with_external_vector_store.bal ::: ::: out rag_query_with_external_vector_store.out ::: ## Related links - [RAG ingestion example](/learn/by-example/rag-ingestion/) - [The `ballerinax/ai.milvus` module](https://central.ballerina.io/ballerinax/ai.milvus/latest) - [The `ballerinax/ai.pinecone` module](https://central.ballerina.io/ballerinax/ai.pinecone/latest) - [The `ballerinax/ai.pgvector` module](https://central.ballerina.io/ballerinax/ai.pgvector/latest) - [The `ballerinax/ai.weaviate` module](https://central.ballerina.io/ballerinax/ai.weaviate/latest) ================================================ FILE: examples/rag-query-with-external-vector-store/rag_query_with_external_vector_store.metatags ================================================ description: This BBE demonstrates how to do retrieval-augmented generation (RAG). keywords: ballerina, ballerina by example, BBE, ai, llm, rag ================================================ FILE: examples/rag-query-with-external-vector-store/rag_query_with_external_vector_store.out ================================================ $ bal run rag_query_with_external_vector_store.bal Query: What is the process for appealing a rejected leave request? Answer: I don't know. Query: How many annual leave days can a full-time employee carry forward to the next year? Answer: A full-time employee can carry forward up to 5 unused annual leave days to the next year. ================================================ FILE: examples/rag-with-in-memory-vector-store/leave_policy.md ================================================ # 📝 Company Leave Policy **Effective Date:** January 1, 2025 **Last Updated:** September 24, 2025 **Applies to:** All full-time and part-time employees --- ## 1. 🎯 Purpose The purpose of this leave policy is to outline the types of leave available to employees and the procedures for requesting time off. We aim to provide a fair and transparent system that supports employee well-being while ensuring continuity of work. --- ## 2. 🕒 Types of Leave ### 2.1. Annual Leave (Paid Time Off - PTO) - **Eligibility:** All full-time employees - **Accrual:** 1.75 days per month (21 days per year) - **Carry Forward:** Up to 5 unused days can be carried forward to the next calendar year - **Usage:** Requires manager approval and minimum 3 days' notice ### 2.2. Sick Leave - **Eligibility:** All employees - **Entitlement:** 12 days per year (non-cumulative) - **Documentation:** Medical certificate required for absences longer than 2 days - **Notification:** Inform the manager within 24 hours of illness ### 2.3. Casual Leave - **Entitlement:** 8 days per year - **Usage:** For short-term personal reasons - **Approval:** Prior approval required where possible ### 2.4. Maternity / Paternity Leave - **Maternity Leave:** Up to 26 weeks (as per applicable law) - **Paternity Leave:** 10 days - **Notification:** At least 8 weeks' advance notice ### 2.5. Bereavement Leave - **Entitlement:** Up to 5 working days - **Eligibility:** Death of an immediate family member - **Proof:** May require documentation ### 2.6. Leave Without Pay (LWP) - **Eligibility:** On request, subject to manager and HR approval - **Usage:** For personal emergencies or extended time off needs - **Impact:** Does not affect job status, but may affect benefits depending on duration --- ## 3. 📅 Leave Request Procedure 1. Submit leave request via the HR portal or email 2. Include: - Type of leave - Dates - Reason (if required) 3. Manager reviews and approves/rejects 4. HR updates leave records --- ## 4. 🔁 Leave Encashment - **Encashment:** Allowed only at the end of the calendar year for up to 5 unused annual leave days - **Eligibility:** Must have completed at least 12 months of continuous service --- ## 5. ❗ Important Notes - Unauthorized absence may lead to disciplinary action - Holidays falling during leave period will not be counted as leave - In case of resignation, encashment or adjustment of unused leaves will be as per final settlement --- ## 6. 📌 Contact For any clarification, please contact: 📧 **hr@sample.com** 📞 **+1-SAM-PLE-LEAVE** --- > *This document is for internal use only and subject to change by management without prior notice.* ================================================ FILE: examples/rag-with-in-memory-vector-store/rag_with_in_memory_vector_store.bal ================================================ import ballerina/ai; import ballerina/io; // Define an in-memory vector store. final ai:VectorStore vectorStore = check new ai:InMemoryVectorStore(); // Define the embedding provider to use. // The example uses the default embedding provider implementation // (with configuration added via a Ballerina VS Code command). final ai:EmbeddingProvider embeddingProvider = check ai:getDefaultEmbeddingProvider(); // Create the knowledge base with the vector store and embedding provider. final ai:KnowledgeBase knowledgeBase = new ai:VectorKnowledgeBase(vectorStore, embeddingProvider); // Define the model provider to use. // The example uses the default model provider implementation // (with configuration added via a Ballerina VS Code command). final ai:ModelProvider modelProvider = check ai:getDefaultModelProvider(); public function main() returns error? { // Ingestion process. // Use data loaders to load documents. ai:DataLoader loader = check new ai:TextDataLoader("./leave_policy.md"); ai:Document|ai:Document[] documents = check loader.load(); // Ingest the documents into the knowledge base. // When `ai:Document`s are ingested, chunking is handled by the knowledge base. // Alternatively, for finer control, you can use the required chunker // (`ai:Chunker` implementations) to chunk documents and pass the chunks // as the argument. check knowledgeBase.ingest(documents); io:println("Ingestion successful"); // Querying process. string query = "How many annual leave days can a full-time employee carry forward to the next year?"; ai:QueryMatch[] queryMatches = check knowledgeBase.retrieve(query, 10); ai:Chunk[] context = from ai:QueryMatch queryMatch in queryMatches select queryMatch.chunk; // The `augmentUserQuery` function augments the user query with the context using // a generic prompt template. ai:ChatUserMessage augmentedQuery = ai:augmentUserQuery(context, query); // Use the `chat` method with the `ai:ChatUserMessage` with the augmented query. ai:ChatAssistantMessage assistantMessage = check modelProvider->chat(augmentedQuery); io:println("\nQuery: ", query); io:println("Answer: ", assistantMessage.content); } ================================================ FILE: examples/rag-with-in-memory-vector-store/rag_with_in_memory_vector_store.md ================================================ # Retrieval-augmented generation (RAG) with an in-memory vector store Ballerina has high-level, provider-agnostic APIs to ingest data for retrieval-augmented generation (RAG) workflows. These include abstractions such as `ai:DataLoader`, `ai:VectorStore`, `ai:EmbeddingProvider`, and `ai:KnowledgeBase`. These abstractions enable you to load documents, convert them into semantically meaningful vector representations using embedding models, and index them into a vector database. Then at generation/querying, you can query semantically similar content from vector databases and use retrieved context in the request to the LLM to generate more accurate responses. The knowledge base (`ai: KnowledgeBase`) orchestrates this process. This example demonstrates implementing RAG workflow using an in-memory vector store. > Note: This example uses the default embedding provider and model provider implementations. 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.` embedding provider implementation. For more information on the underlying module, see the [`ballerina/ai` module](https://lib.ballerina.io/ballerina/ai/latest/). ::: code rag_with_in_memory_vector_store.bal ::: ::: out rag_with_in_memory_vector_store.out ::: ## Related links - [Sample policy document](https://github.com/ballerina-platform/ballerina-distribution/tree/master/examples/rag-with-in-memory-vector-store/leave_policy.md) - [RAG ingestion with external vector store example](/learn/by-example/rag-ingestion-with-external-vector-store/) - [RAG query with external vector store example](/learn/by-example/rag-query-with-external-vector-store/) - [The `ballerinax/ai.milvus` module](https://central.ballerina.io/ballerinax/ai.milvus/latest) - [The `ballerinax/ai.pinecone` module](https://central.ballerina.io/ballerinax/ai.pinecone/latest) - [The `ballerinax/ai.pgvector` module](https://central.ballerina.io/ballerinax/ai.pgvector/latest) - [The `ballerinax/ai.weaviate` module](https://central.ballerina.io/ballerinax/ai.weaviate/latest) ================================================ FILE: examples/rag-with-in-memory-vector-store/rag_with_in_memory_vector_store.metatags ================================================ description: This BBE demonstrates how to do data ingestion for retrieval-augmented generation (RAG). keywords: ballerina, ballerina by example, BBE, ai, llm, rag ================================================ FILE: examples/rag-with-in-memory-vector-store/rag_with_in_memory_vector_store.out ================================================ $ bal run rag_with_in_memory_vector_store.bal Ingestion successful Query: How many annual leave days can a full-time employee carry forward to the next year? Answer: A full-time employee can carry forward up to 5 unused annual leave days to the next year. ================================================ FILE: examples/random-numbers/random_numbers.bal ================================================ import ballerina/io; import ballerina/random; public function main() returns error? { // Generates a random decimal number between 0.0 and 1.0. float randomDecimal = random:createDecimal(); io:println("Random decimal number: ", randomDecimal); // Generates a random number between the given start(inclusive) and end(exclusive) values. int randomInteger = check random:createIntInRange(1, 100); io:println("Random integer number in range: ", randomInteger); } ================================================ FILE: examples/random-numbers/random_numbers.md ================================================ # Random numbers The `random` library provides functions related to random number generation. For more information on the underlying module, see the [`random` module](https://lib.ballerina.io/ballerina/random/latest/). ::: code random_numbers.bal ::: To run this sample, use the `bal run` command. ::: out random_numbers.out ::: ================================================ FILE: examples/random-numbers/random_numbers.metatags ================================================ description: BBE on how to to generate random numbers. keywords: ballerina, ballerina by examples, bbe, random ================================================ FILE: examples/random-numbers/random_numbers.out ================================================ $ bal run random_numbers.bal Random decimal number: 0.6146990788006506 Random integer number in range: 94 ================================================ FILE: examples/raw-templates/raw_templates.bal ================================================ import ballerina/io; function col3() returns boolean { return false; } type MyCSVRawTemplate object { *object:RawTemplate; public (string[] & readonly) strings; public [int, int, boolean] insertions; }; public function main() { int col1 = 5; int col2 = 10; // Any value is allowed as an interpolation when defining a value of the `object:RawTemplate` type // since it has `(any|error)[]` as the `insertions` type. object:RawTemplate rawTemplate = `${col1}, fixed_string1, ${col2}, ${col3()}, fixed_string3`; io:println(rawTemplate.strings); io:println(rawTemplate.insertions); // With the custom `MyCSVRawTemplate ` raw template type, the compiler // expects two integers followed by a boolean value as interpolations. MyCSVRawTemplate myCSVRawTemplate = `fixed_string4, ${col1}, ${col2}, fixed_string_5, ${col3()}`; io:println(myCSVRawTemplate.strings); io:println(myCSVRawTemplate.insertions); } ================================================ FILE: examples/raw-templates/raw_templates.md ================================================ # Raw templates Raw template expressions are backtick templates without a tag (such as `string` or `xml`). This is a sequence of characters interleaved with interpolations within a pair of backticks (in the form `${expression}`). The result of evaluating such a raw template is an `object:RawTemplate` object that has two fields `(readonly & string[]) strings` and `(any|error)[] insertions`. The `strings` array will have string literals in the backtick string broken at interpolations and the `insertions` array will have the resultant values of evaluating each interpolation. If you want to control the type of the strings or the interpolations more precisely, you can define an object type that includes the `object:RawTemplate` type and override the relevant field(s) with narrower types. Then, the compiler will statically validate the values against the expected type(s). An important use case of custom raw templates is SQL parameterized queries. ::: code raw_templates.bal ::: ::: out raw_templates.out ::: ## Related links - [String templates](https://ballerina.io/learn/by-example/string-templates/) - [RegExp type](https://ballerina.io/learn/by-example/regexp-type/) - [Object type inclusion](https://ballerina.io/learn/by-example/object-type-inclusion/) - [Database Access - Simple query](https://ballerina.io/learn/by-example/mysql-query-operation/) ================================================ FILE: examples/raw-templates/raw_templates.metatags ================================================ description: This BBE demonstrates raw templates in Ballerina. keywords: ballerina, ballerina by example, bbe, raw templates ================================================ FILE: examples/raw-templates/raw_templates.out ================================================ $ bal run ["",", fixed_string1, ",", ",", fixed_string3"] [5,10,false] ["fixed_string4, ",", ",", fixed_string_5, ",""] [5,10,false] ================================================ FILE: examples/readonly-and-isolated/readonly_and_isolated.bal ================================================ import ballerina/io; type Entry map; type RoMap readonly & map; final RoMap m = loadMap(); function loadMap() returns RoMap { readonly & Entry entry1 = { "munich": {latitude: "48.1351N", longitude: "11.5820E"}, "berlin": {latitude: "52.5200N", longitude: "13.4050E"} }; readonly & Entry entry2 = { "bordeaux": {latitude: "44.8378N", longitude: "0.5792W"}, "paris": {latitude: "48.8566N", longitude: "2.3522E"} }; RoMap roMap = {"germany": entry1, "france": entry2}; return roMap; } isolated function lookup(string s) returns readonly & Entry? { // Accesses `m` directly without locking. return m[s]; } public function main() { io:println(lookup("france")); } ================================================ FILE: examples/readonly-and-isolated/readonly_and_isolated.md ================================================ # Readonly and isolated `isolated` functions can access `final` variables with `readonly` type without locking. It relies on the fact that immutability is deep. `isolated` for functions complements `readonly` for data. ::: code readonly_and_isolated.bal ::: Executing the above code gives the output below. ::: out readonly_and_isolated.out ::: ================================================ FILE: examples/readonly-and-isolated/readonly_and_isolated.metatags ================================================ description: This BBE introduces readonly and isolated in Ballerina. keywords: ballerina, ballerina by example, bbe, readonly, isolated ================================================ FILE: examples/readonly-and-isolated/readonly_and_isolated.out ================================================ $ bal run readonly_and_isolated.bal {"bordeaux":{"latitude":"44.8378N","longitude":"0.5792W"},"paris":{"latitude":"48.8566N","longitude":"2.3522E"}} ================================================ FILE: examples/readonly-objects-and-classes/readonly_objects_and_classes.bal ================================================ import ballerina/io; // The `Timezone` is a `readonly` object type. type TimeZone readonly & object { function getOffset(decimal utc) returns decimal; }; readonly class FixedTimeZone { // Include the `readonly` object type named `TypeZone` using object type inclusion. *TimeZone; // The `final decimal` field named offset is read-only because the `decimal` basic type is // inherently immutable. final decimal offset; function init(decimal offset) { self.offset = offset; } function getOffset(decimal utc) returns decimal { return self.offset; } } public function main() { // Create a new class object. FixedTimeZone timeZone = new(+5.30); io:println(timeZone is FixedTimeZone); io:println(timeZone is readonly & FixedTimeZone); } ================================================ FILE: examples/readonly-objects-and-classes/readonly_objects_and_classes.md ================================================ # Readonly Objects and Classes An object is `readonly` if all of its fields are `final` and are of types that are subtypes of the `readonly` type. Object `T` can be declared as readonly with `readonly & T`. A class that belongs to the `readonly` type can be declared by prefixing the `readonly` keyword in the class declaration. ::: code readonly_objects_and_classes.bal ::: ::: out readonly_objects_and_classes.out ::: ================================================ FILE: examples/readonly-objects-and-classes/readonly_objects_and_classes.metatags ================================================ description: This BBE introduces read-only objects and classes in Ballerina. keywords: ballerina, ballerina by example, bbe, readonly type, final, object, class ================================================ FILE: examples/readonly-objects-and-classes/readonly_objects_and_classes.out ================================================ $ bal run readonly_objects_and_classes.bal true true ================================================ FILE: examples/readonly-type/readonly_type.bal ================================================ import ballerina/io; // A `const` is immutable. const s = "Anne"; type Row record { // Both the field and its value are immutable. readonly string k; int value; }; table key(k) t = table [ {k: "John", value: 17} ]; public function main() { // Can safely use `s` as a key. t.add({k: s, value: 18}); io:println(t); } ================================================ FILE: examples/readonly-type/readonly_type.md ================================================ # `readonly` type The `readonly` type consists of values that are immutable. For structural type `T`, `T & readonly` means immutable `T`. `T & readonly` is a subtype of `T` and a subtype of `readonly`. Guaranteed that if declared type of a value is a subtype of `readonly`, then at runtime the value can never be mutated. It is enforced by runtime checks on the mutating structures. With `readonly` field, both the field and its value are immutable. ::: code readonly_type.bal ::: Executing the above code gives the output below. ::: out readonly_type.out ::: ================================================ FILE: examples/readonly-type/readonly_type.metatags ================================================ description: This BBE introduces readonly type in Ballerina. keywords: ballerina, ballerina by example, bbe, readonly type ================================================ FILE: examples/readonly-type/readonly_type.out ================================================ $ bal run readonly_type.bal [{"k":"John","value":17},{"k":"Anne","value":18}] ================================================ FILE: examples/receive-email-using-client/receive_email_using_client.bal ================================================ import ballerina/email; import ballerina/log; public function main() returns error? { email:ImapClient imapClient = check new ("imap.email.com", "reader@email.com", "pass456"); do { while true { // Reads the first unseen email received by the POP3 server. // `()` is returned when there are no new unseen emails. 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/receive-email-using-client/receive_email_using_client.md ================================================ # Email client - Receive email The `email:ImapClient` can receive emails from an email server via IMAP protocol. An `email:ImapClient` can be created by providing the hostname, username, and password. Once connected, `receiveMessage` method is used to receive emails from the server. It is a blocking method with a timeout. When the email server supports both POP3 and IMAP, it is recommended to use the IMAP as it provides the ability to manage emails using multiple devices or email clients, while allowing access to emails that are already read. >**Note:** The Ballerina `email` module also provides an `email:PopClient` which can be used likewise. The only difference is that the `email:PopClient` uses POP3 protocol for communication. ::: code receive_email_using_client.bal ::: ## Prerequisites - The email server should be up and running. Run the sample code by executing the following command. ::: out receive_email_using_client.out ::: ## Related links - [`email:ImapClient` client object - API documentation](https://lib.ballerina.io/ballerina/email/latest#ImapClient) - [`email:PopClient` client object - API documentation](https://lib.ballerina.io/ballerina/email/latest#PopClient) - [IMAP client - Specification](https://ballerina.io/spec/email/#33-imap-client) - [POP3 client - Specification](https://ballerina.io/spec/email/#32-pop3-client) ================================================ FILE: examples/receive-email-using-client/receive_email_using_client.metatags ================================================ description: This is a BBE on receiving emails. It has the client functionality for receiving emails. keywords: ballerina, ballerina by example, bbe, email, POP3, POP, IMAP ================================================ FILE: examples/receive-email-using-client/receive_email_using_client.out ================================================ $ bal run receive_email_using_client.bal ================================================ FILE: examples/receive-email-using-service/receive_email_using_service.bal ================================================ import ballerina/email; import ballerina/log; // Creates the listener with the connection parameters and the protocol-related configuration. listener email:ImapListener emailListener = new ({ host: "imap.email.com", username: "reader@email.com", password: "pass456" }); // One or many services can listen to the email listener for the periodically-polled emails. service "observer" on emailListener { // When an email is successfully received, the `onMessage` method is called. remote function onMessage(email:Message email) { log:printInfo("Received an email", subject = email.subject, content = email?.body); } // When an error occurs during the email poll operations, the `onError` method is called. remote function onError(email:Error emailError) { log:printError(emailError.message(), stackTrace = emailError.stackTrace()); } // When the listener is closed, the `onClose` method is called. remote function onClose(email:Error? closeError) { if closeError is email:Error { log:printInfo(closeError.message(), stackTrace = closeError.stackTrace()); } } } ================================================ FILE: examples/receive-email-using-service/receive_email_using_service.md ================================================ # Email service - Receive email The `email:Service` receives messages from an email server via IMAP using the `email:ImapListener`. An `email:ImapListener` can be initialized by providing the hostname, username, and password. Once connected, `onMessage` method is invoked whenever an email is read from the email server. If there is an error while reading the data from the server, `onError` method is invoked with relevant error details. When the email server supports both POP3 and IMAP, it is recommended to use the IMAP as it provides the ability to manage emails using multiple devices or email clients, while allowing access to emails that are already read. >**Note:** The Ballerina `email` module also provides an `email:PopListener` which can be used likewise. The only difference is that the `email:PopListener` uses POP3 protocol for communication. ::: code receive_email_using_service.bal ::: ## Prerequisites - Email server should be up and running. Run the email service by executing the following command. ::: out receive_email_using_service.out ::: ## Related links - [`email:ImapListener` listener object - API documentation](https://lib.ballerina.io/ballerina/email/latest#ImapListener) - [`email:PopListener` listener object - API documentation](https://lib.ballerina.io/ballerina/email/latest#PopListener) - [Email service - Specification](https://ballerina.io/spec/email/#4-service) ================================================ FILE: examples/receive-email-using-service/receive_email_using_service.metatags ================================================ description: This BBE is about receiving emails. It has a listener-based functionality for receiving emails. keywords: ballerina, ballerina by example, bbe, email, POP3, POP, IMAP, listener ================================================ FILE: examples/receive-email-using-service/receive_email_using_service.out ================================================ $ bal run receive_email_using_listener.bal ================================================ FILE: examples/record-to-edi/bal_project.out ================================================ $ bal new record_to_edi $ cd record_to_edi $ bal add sorder ================================================ FILE: examples/record-to-edi/codegen_command.out ================================================ $ bal edi codegen -i resources/simple_order_schema.json -o modules/sorder/sorder.bal ================================================ FILE: examples/record-to-edi/output.out ================================================ $ bal run HDR*ORDER_200*HMart*17-05-2023~ ITM*A680*15~ ITM*A530*2~ ITM*A500*4~ ================================================ FILE: examples/record-to-edi/package_structure.out ================================================ └── record_to_edi ├── Ballerina.toml ├── Dependencies.toml ├── main.bal ├── modules │   └── sorder │   ├── Module.md │   ├── resources │   ├── sorder.bal │   └── tests │   └── lib_test.bal └── resources └── simple_order_schema.json ================================================ FILE: examples/record-to-edi/record_to_edi.bal ================================================ import ballerina/io; import record_to_edi.sorder; public function main() returns error? { sorder:SimpleOrder simpleOrder = {header: {code: "HDR", orderId: "ORDER_200", organization: "HMart", date: "17-05-2023"}}; simpleOrder.items.push({code: "ITM", item: "A680", quantity: 15}); simpleOrder.items.push({code: "ITM", item: "A530", quantity: 2}); simpleOrder.items.push({code: "ITM", item: "A500", quantity: 4}); string ediText = check sorder:toEdiString(simpleOrder); io:println(ediText); } ================================================ FILE: examples/record-to-edi/record_to_edi.md ================================================ # Record to EDI conversion Same EDI schema and generated code used in EDI to record conversion example can be used to convert Ballerina records of type SimpleOrder to EDI. ::: code schema.json ::: Create a new Ballerina project named `record_to_edi` 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 convert Ballerina records to EDI. ::: code record_to_edi.bal ::: Run the program using the command below: ::: out output.out ::: ================================================ FILE: examples/record-to-edi/record_to_edi.metatags ================================================ description: This BBE demonstrates how convert Ballerina records into EDI text based on an EDI schema. keywords: ballerina, ballerina by example, bbe, edi, electronic data interchange, b2b, integration ================================================ FILE: examples/record-to-edi/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/record-to-edi/tool_pull_command.out ================================================ $ bal tool pull edi ================================================ FILE: examples/records/records.bal ================================================ import ballerina/io; // Defines a `closed record` type named `Student`. It only allows fields that are specified. type Student record {| string name; int age; |}; // Defines an `open record` type named `Employee`. It allows fields other than those specified. type Employee record { string name; int age; }; public function main() { // Creates an `open record` by specifying values for its fields. record {string name; int age;} person = { name: "Harry", age: 12 }; // Creates a record using the `Student` type definition. Student student = { name: "Harry", age: 12 }; // Creates a record using the `Employee` type definition with an additional `country` field. Employee employee = { name: "Harry", age: 12, "country": "UK" }; // Accesses the `name` field in `employee`. string name = employee.name; io:println(name); // The two `person` & `student` records are equal since they have the same set of fields // and values. io:println(person == student); // Record equality returns `false` on the following two records as `employee` has // an additional field called `country`. io:println(person == employee); } ================================================ FILE: examples/records/records.md ================================================ # Records A `record` type is a collection of specific named fields where each field has a type for its value. A field `f` of a record value `r` can be accessed with `r.f`. Records are mutable and can be constructed using a syntax similar to a map. A `closed record` type only allows fields that are specified whereas, an `open record` type allows additional fields other than those specified. Typically, a `record` type is written using a type definition. ::: code records.bal ::: ::: out records.out ::: ## Related links - [Open records](/learn/by-example/open-records/) - [Controlling openness](/learn/by-example/controlling-openness/) - [Default values for record fields](/learn/by-example/default-values-for-record-fields/) - [Maps](/learn/by-example/maps/) ================================================ FILE: examples/records/records.metatags ================================================ description: This BBE demonstrates how to define a record, access record fields, record type definition, and record equality. keywords: ballerina, ballerina by example, bbe, records, record field, open record, closed record ================================================ FILE: examples/records/records.out ================================================ $ bal run records.bal Harry true false ================================================ FILE: examples/regexp-find-operations/regexp_find_operations.bal ================================================ import ballerina/io; import ballerina/lang.regexp; public function main() returns error? { string logContent = string ` 2024-09-19 10:02:01 WARN [UserLogin] - Failed login attempt for user: johndoe 2024-09-19 10:03:17 ERROR [Database] - Connection to database timed out 2024-09-19 10:04:05 WARN [RequestHandler] - Response time exceeded threshold for /api/v1/users 2024-09-19 10:05:45 INFO [Scheduler] - Scheduled task started: Data backup 2024-09-19 10:06:10 ERROR [Scheduler] - Failed to start data backup: Permission denied 2024-09-19 10:11:55 INFO [Security] - Security scan completed, no issues found 2024-09-19 10:12:30 ERROR [RequestHandler] - 404 Not Found: /api/v1/products`; // Regular expression to match error logs with three groups: // 1. Timestamp (e.g., 2024-09-19 10:03:17). // 2. Component (e.g., Database, Scheduler). // 3. Log message (e.g., Connection to database timed out). string:RegExp errorLogPattern = re `(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) ERROR \[(\w+)\]\s-\s(.*)`; // Retrieve the first error log from `logContent`. regexp:Span? firstErrorLog = errorLogPattern.find(logContent); if firstErrorLog == () { io:println("Failed to find a error log"); return; } io:println("First error log: ", firstErrorLog.substring()); // Retrieve all error logs from the `logContent`. regexp:Span[] allErrorLogs = errorLogPattern.findAll(logContent); io:println("\nAll error logs:"); foreach regexp:Span errorLog in allErrorLogs { io:println(errorLog.substring()); } // Retrieve groups (timestamp, component, message) from the first error log. regexp:Groups? firstErrorLogGroups = errorLogPattern.findGroups(logContent); if firstErrorLogGroups == () { io:println("Failed to find groups in first error log"); return; } io:println("\nGroups within first error log:"); check printGroupsWithinLog(firstErrorLogGroups); // Retrieve groups from all error logs. regexp:Groups[] allErrorLogGroups = errorLogPattern.findAllGroups(logContent); io:println("\nGroups in all error logs"); foreach regexp:Groups logGroup in allErrorLogGroups { check printGroupsWithinLog(logGroup); } } function printGroupsWithinLog(regexp:Groups logGroup) returns error? { // The first element in the `logGroup` is the entire matched string. // The subsequent elements in `logGroup` represent the captured groups // (timestamp, component, message). string timestamp = (check logGroup[1].ensureType(regexp:Span)).substring(); string component = (check logGroup[2].ensureType(regexp:Span)).substring(); string logMessage = (check logGroup[3].ensureType(regexp:Span)).substring(); io:println("Timestamp: ", timestamp); io:println("Component: ", component); io:println("Message: ", logMessage); } ================================================ FILE: examples/regexp-find-operations/regexp_find_operations.md ================================================ # RegExp find operations The `RegExp` type provides a set of langlib functions to find patterns within strings. These functions enable efficient pattern matching, grouping, and extraction based on specific regular expressions. ::: code regexp_find_operations.bal ::: ::: out regexp_find_operations.out ::: ## Related links - [RegExp type](/learn/by-example/regexp-type) - [RegExp API Docs](https://lib.ballerina.io/ballerina/lang.regexp) - [string API Docs](https://lib.ballerina.io/ballerina/lang.string) ================================================ FILE: examples/regexp-find-operations/regexp_find_operations.metatags ================================================ description: This BBE demonstrates how to use the regexp langlib functions relevant to regex find operations. keywords: ballerina, ballerina by example, bbe, regexp, RegExp, regex, regular expressions, ballerina regex functions, regexp langlib functions, find, findAll, findGroups, findAllGroups ================================================ FILE: examples/regexp-find-operations/regexp_find_operations.out ================================================ $ bal run regexp_find_operations.bal First error log: 2024-09-19 10:03:17 ERROR [Database] - Connection to database timed out All error logs: 2024-09-19 10:03:17 ERROR [Database] - Connection to database timed out 2024-09-19 10:06:10 ERROR [Scheduler] - Failed to start data backup: Permission denied 2024-09-19 10:12:30 ERROR [RequestHandler] - 404 Not Found: /api/v1/products Groups within first error log: Timestamp: 2024-09-19 10:03:17 Component: Database Message: Connection to database timed out Groups in all error logs Timestamp: 2024-09-19 10:03:17 Component: Database Message: Connection to database timed out Timestamp: 2024-09-19 10:06:10 Component: Scheduler Message: Failed to start data backup: Permission denied Timestamp: 2024-09-19 10:12:30 Component: RequestHandler Message: 404 Not Found: /api/v1/products ================================================ FILE: examples/regexp-match-operations/regexp_match_operations.bal ================================================ import ballerina/io; import ballerina/lang.regexp; public function main() returns error? { string[] productCodes = ["ELEC-1234", "FURN-5678", "CLOT-1111", "FOOD-3333", "BAR-123"]; // Regular expression to match and validate product codes. // Format: [PRODUCT_TYPE]-[UNIQUE_ID] string:RegExp productCodePattern = re `^([A-Z]{4})-(\d{4})$`; foreach string productCode in productCodes { // Check if the product code fully matches the expected format. boolean isValidProductCode = productCodePattern.isFullMatch(productCode); io:println(string `Product Code: ${productCode}, Valid: ${isValidProductCode}`); // If the product code is invalid, skip further processing. if !isValidProductCode { continue; } // For a valid product code, extract the product type and unique ID from the match groups. regexp:Groups? matchGroups = productCodePattern.fullMatchGroups(productCode); if matchGroups is regexp:Groups { // The first member in the `matchGroups` tuple is the entire matched string. // The subsequent members represent the captured groups // corresponding to product type and unique ID respectively. io:println("Product Type: ", (check matchGroups[1].ensureType(regexp:Span)).substring()); io:println("Unique ID: ", (check matchGroups[2].ensureType(regexp:Span)).substring()); } } // Match product code from a specific starting index in the string. regexp:Span? productCode = productCodePattern.matchAt( "Product code: FURN-5678, Product code: CLOT-1234", 39); if productCode is regexp:Span { io:println("Matched product: ", productCode.substring()); } // Regular expression to extract the time in the format `HH:MM` from a log string. string:RegExp timePattern = re `([01][0-9]|2[0-3]):([0-5][0-9])`; // Find groups of the matching string from a specified starting index. regexp:Groups? timeMatchGroups = timePattern.matchGroupsAt( "Production time: 14:35, Production time: 16:15", 41); if timeMatchGroups is regexp:Groups { string hour = (check timeMatchGroups[1].ensureType(regexp:Span)).substring(); string minutes = (check timeMatchGroups[2].ensureType(regexp:Span)).substring(); io:println("Production hour: ", hour); io:println("Production minutes: ", minutes); } } ================================================ FILE: examples/regexp-match-operations/regexp_match_operations.md ================================================ # RegExp match operations The `RegExp` type supports a set of langlib functions to match patterns in strings and enable operations such as finding, validating, grouping, and extracting data based on regular expressions. ::: code regexp_match_operations.bal ::: ::: out regexp_match_operations.out ::: ## Related links - [RegExp type](/learn/by-example/regexp-type) - [RegExp API Docs](https://lib.ballerina.io/ballerina/lang.regexp) - [string API Docs](https://lib.ballerina.io/ballerina/lang.string) ================================================ FILE: examples/regexp-match-operations/regexp_match_operations.metatags ================================================ description: This BBE demonstrates how to use the regexp langlib functions relevant to regex match operations. keywords: ballerina, ballerina by example, bbe, regexp, RegExp, regex, regular expressions, ballerina regex functions, regexp langlib functions, fullMatchGroups, isFullMatch, matchAt, matchGroupsAt ================================================ FILE: examples/regexp-match-operations/regexp_match_operations.out ================================================ $ bal run regexp_match_operations.bal Product Code: ELEC-1234, Valid: true Product Type: ELEC Unique ID: 1234 Product Code: FURN-5678, Valid: true Product Type: FURN Unique ID: 5678 Product Code: CLOT-1111, Valid: true Product Type: CLOT Unique ID: 1111 Product Code: FOOD-3333, Valid: true Product Type: FOOD Unique ID: 3333 Product Code: BAR-123, Valid: false Matched product: CLOT-1234 Production hour: 16 Production minutes: 15 ================================================ FILE: examples/regexp-operations-overview/regexp_operations_overview.bal ================================================ import ballerina/lang.regexp; import ballerina/io; public function main() returns error? { string data = "bob@example.net,alice@example.com,charlie@example.org,david@example.xyz,invalid#@example.n%t"; // Split the comma-separated email list. string[] emails = re `,`.split(data); io:println(emails); // Define a RegExp pattern to match valid email addresses. string:RegExp validEmailPattern = re `([a-zA-Z0-9._%+-]+)@([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})`; // Extract usernames and domains from valid email addresses. string[] usernames = []; string[] domains = []; foreach string email in emails { // Check if the email is valid. if validEmailPattern.isFullMatch(email) { // Extract the username and domain from the email. regexp:Groups? emailGroups = validEmailPattern.findGroups(email); if emailGroups is regexp:Groups { // 0th element contains the entire match usernames.push((emailGroups[1]).substring()); domains.push((emailGroups[2]).substring()); } } } io:println(usernames); io:println(domains); } ================================================ FILE: examples/regexp-operations-overview/regexp_operations_overview.md ================================================ # RegExp operations The ``RegExp`` type supports a set of langlib functions to search and manipulate string values. ::: code regexp_operations_overview.bal ::: ::: out regexp_operations_overview.out ::: ## Related links - [RegExp type](/learn/by-example/regexp-type) - [RegExp API Docs](https://lib.ballerina.io/ballerina/lang.regexp) - [string API Docs](https://lib.ballerina.io/ballerina/lang.string) ================================================ FILE: examples/regexp-operations-overview/regexp_operations_overview.metatags ================================================ description: This BBE demonstrates how to use langlib functions defined in the regexp langlib module. keywords: ballerina, ballerina by example, bbe, regexp, RegExp, regex, regular expressions, ballerina regex functions, regexp langlib functions ================================================ FILE: examples/regexp-operations-overview/regexp_operations_overview.out ================================================ $ bal run regexp_operations_overview.bal ["bob@example.net","alice@example.com","charlie@example.org","david@example.xyz","invalid#@example.n%t"] ["bob","alice","charlie","david"] ["example.net","example.com","example.org","example.xyz"] ================================================ FILE: examples/regexp-replace-operations/regexp_replace_operations.bal ================================================ import ballerina/io; public function main() { string creditCardNumber = "1234-5678-9876-5432"; string:RegExp pattern = re `\d{4}-\d{4}-\d{4}`; // Replace the first occurrence of the credit card pattern with a masked representation. string maskedCardNumber = pattern.replace(creditCardNumber, "****-****-****"); io:println(maskedCardNumber); xml xmlString = xml `value1value2>`; // Regular expression to match XML comments. string:RegExp commentPattern = re ``; // Replace all occurrences of XML comments with an empty string, effectively removing them. string commentsRemovedXml = commentPattern.replaceAll(xmlString.toString(), ""); io:println("XML string with comments removed: ", commentsRemovedXml); } ================================================ FILE: examples/regexp-replace-operations/regexp_replace_operations.md ================================================ # RegExp replace operations The `RegExp` type supports a set of langlib functions to replace parts of strings that match specific patterns. ::: code regexp_replace_operations.bal ::: ::: out regexp_replace_operations.out ::: ## Related links - [RegExp type](/learn/by-example/regexp-type) - [RegExp API Docs](https://lib.ballerina.io/ballerina/lang.regexp) - [string API Docs](https://lib.ballerina.io/ballerina/lang.string) ================================================ FILE: examples/regexp-replace-operations/regexp_replace_operations.metatags ================================================ description: This BBE demonstrates how to use the regexp langlib functions relevant to regex replace operations. keywords: ballerina, ballerina by example, bbe, regexp, RegExp, regex, regular expressions, ballerina regex functions, regexp langlib functions, replace, replaceAll ================================================ FILE: examples/regexp-replace-operations/regexp_replace_operations.out ================================================ $ bal run regexp_replace_operations.bal ****-****-****-5432 XML string with comments removed: value1value2> ================================================ FILE: examples/regexp-type/regexp_type.bal ================================================ import ballerina/io; import ballerina/lang.regexp; public function main() returns error? { // Declare a `RegExp` value using a regular expression literal. regexp:RegExp alphanumericPattern = re `[a-zA-Z0-9]`; // Matches any string that is a single alphanumeric character. io:println("Pattern `[a-zA-Z0-9]` matches `a`: ", alphanumericPattern.isFullMatch("a")); // `string:RegExp` is an alias for `regexp:RegExp`. string:RegExp alphanumericPattern2 = re `[a-zA-Z0-9]`; io:println("Pattern `[a-zA-Z0-9]` matches `a1`: ", alphanumericPattern2.isFullMatch("a1")); // Construct a `RegExp` value from a string using the `fromString` langlib function. string:RegExp pattern = check regexp:fromString("HELLO*"); // Matches any string that starts with "HELLO" and ends with zero or more characters. io:println("Pattern `HELLO*` matches `HELLO`: ", pattern.isFullMatch("HELLO")); // Matches any string that contains one or more consecutive "a" characters. boolean result = re `a+`.isFullMatch("aaa"); io:println("Pattern `a+` matches `aaa`: ", result); // Interpolations can be used to construct the pattern dynamically. string literal = "xyz"; string:RegExp pattern2 = re `abc|${literal}`; // Matches any string that starts with "abc" or "XYZ". io:println("Pattern `abc|${\"xyz\"}` matches `xyz`: ", pattern2.isFullMatch("xyz")); // The `RegExp` type supports Unicode general category patterns. // Characters are matched based on their Unicode properties. string:RegExp pattern3 = re `\p{Ll}`; io:println("Pattern `\\p{Ll}` matches `a`: ", "a".matches(pattern3)); // The `\P` escape sequence is used to get the negation of the pattern. // Matches every non-puncutation character. string:RegExp pattern4 = re `\P{P}`; io:println("Pattern `\\p{P}` matches `0`: ", "0".matches(pattern4)); // The `RegExp` type supports matching characters according to the script using Unicode script patterns. string:RegExp pattern5 = re `\p{sc=Latin}`; regexp:Span? findResult = pattern5.find("aεЛ"); // The `find` function returns nil if no match is found. Since we know a // match is found here, use the `ensureType` function to narrow the type down to `regexp:Span`. regexp:Span latinValue = check findResult.ensureType(); io:println("Pattern `\\p{sc=Latin}` matches `aεЛ`: ", latinValue.substring()); // The `RegExp` type supports non-capturing groups to control the behavior of regular expression patterns. // The `i` flag will ignore the case of the pattern. string:RegExp pattern6 = re `(?i:BalleRINA)`; io:println("Pattern `(?i:BalleRINA)` matches `Ballerina`: ", "Ballerina".matches(pattern6)); } ================================================ FILE: examples/regexp-type/regexp_type.md ================================================ # RegExp type `RegExp` is a built-in type in Ballerina that represents a regular expression. Regular expressions are commonly used for tasks such as validating input, searching and replacing text, and parsing data. The `RegExp` type is defined in the `lang.regexp` module, and can also be referenced using the type alias named the same in the `lang.string` module. The `RegExp` type conforms to a subset of the ECMAScript specification for regular expressions. A `RegExp` value can be created by using the regexp template expression or calling the [`fromString` method of the lang.regexp](https://lib.ballerina.io/ballerina/lang.regexp/latest#fromString) module. ::: code regexp_type.bal ::: ::: out regexp_type.out ::: ## Related links - [Ballerina regular expression grammar](https://ballerina.io/spec/lang/master/#section_10.1) - [RegExp langlib functions overview](/learn/by-example/regexp-operations-overview) - [RegExp API Docs](https://lib.ballerina.io/ballerina/lang.regexp) - [string API Docs](https://lib.ballerina.io/ballerina/lang.string) ================================================ FILE: examples/regexp-type/regexp_type.metatags ================================================ description: This BBE demonstrates how to define a regular expression and the different patterns supported by the Ballerina RegExp type. keywords: ballerina, ballerina by example, bbe, regex, regexp, regular expression ================================================ FILE: examples/regexp-type/regexp_type.out ================================================ $ bal run regexp_type.bal Pattern `[a-zA-Z0-9]` matches `a`: true Pattern `[a-zA-Z0-9]` matches `a1`: false Pattern `HELLO*` matches `HELLO`: true Pattern `a+` matches `aaa`: true Pattern `abc|${"xyz"}` matches `xyz`: true Pattern `\p{Ll}` matches `a`: true Pattern `\p{P}` matches `0`: true Pattern `\p{sc=Latin}` matches `aεЛ`: a Pattern `(?i:BalleRINA)` matches `Ballerina`: true ================================================ FILE: examples/resource/path/to/client-private.key ================================================ -----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCBXKLp9WJUuJko SfDn3HM2LWVxiHrP11me6D4X2AqoUiCZ1w976q1LuBlzWNhONYwJvHXewUuRmo0d 4NRp2ma0qjdMN246XWyozpzPfJdvovfiT+V6HnSbMg82ZppfF3e85Cgrg6mSA7Wf faaHr62SnexseZ9ioNCuRUrW2Zg208l99IItP4K4iGclQlyjs3S6drH7JJk3rv8B f76wmmoCEAhknOtGLq543dEUIjFgNdIy/GsSY52fiA4LSjUCv27kGn03nayqoZs3 XHqM0qLdt9SPXbYe7l75j0o6Z9tyGFCKXY/zVwiLfRUqDIOfkN5YS4Ohit52Tj7q oBmvEzD7AgMBAAECggEAXM/F4u23OummmQ1T1kaIMpqnaalt06jCGAywYBMUsmca FMYDyfg5lVXkjKl1p8crTeD1AHjWawTjskgYnkmf3ocxXXF3mFBnIUX7o7HURLg7 +RcxoUgwiRiFaZZ7szX3JoLbfzzbcHNQ37kavccBVWwQsFMiU3Tlw+LbKwK6/row LYsQPx7gT4u7hViat4vQDTYcgyjvvFCiek4ndL6O9K49MxIMU678UXB6ia5iUevy vgEfcYkKQ5EQ38qS3ZwsubPvj4633jvAJRr/hJD8XINZC74kTXeV3BGH2LlpQOEq kWkOypwYNjnXtt1JO8+Iu6mEXKUoiIBPfGrJ3vDSQQKBgQDmYPc7kfYan/LHjJRv iE2CwbC26yVA6+BEPQv9z7jChO9Q6cUbGvM8EEVNpC9nmFogkslzJhz55HP84QZL u3ptU+D96ncq6zkBqxBfRnZG++D36+XRXIwzz3h+g1Nwrl0y0MFbwlkMm3ZqJdd6 pZz1FZGd6zvQftW8m7jPSKHuswKBgQCPv6czFOZR6bI+qCQdaORpe9JGoAduOD+4 YKl96s0eiAKhkGhFCrMd6GJwWRkpNcfwB+J9sMahORbfvwiYanI56h7Vi30DFPRb m1m8dLkr6z+8bxMxKJaMXIIjy3UDamgDr7QHInNUih2iGvtB8QqZ0aobsB2XIxZg qESTMcpYmQKBgHSwSqneraQgvgz7FLhFdtUzHDoacr0mfGqz7R37F99XDAyUy+SF ywvyRdgkwGodjhEPqH/tnyGn6GP+6nxzknhL0xtppkCT8kT5C4rmmsQrknChCL/5 u34GqUaTaDEb8FLrz/SVRRuQpvLvBey2dADjkuVFH//kLoig64P6iyLnAoGBAIlF g+2L78YZXVXoS1SqbjUtQUigWXgvzunLpQ/Rwb9+MsUGmgwUg6fz2s1eyGBKM3xM i0VsIsKjOezBCPxD6oDTyk4yvlbLE+7HE5KcBJikNmFD0RgIonu3e6+jA0MXweyD RW/qviflHRdInNgDzxPE3KVEMX26zAvRpGrMCWdBAoGAdQ5SvX+mAC3cKqoQ9Zal lSqWoyjfzP5EaVRG8dtoLxbznQGTTvtHXc65/MznX/L9qkWCS6Eb4HH5M3hFNY46 LNIzGQLznE1odwv7H5B8c0/m3DrKTxbh8bYcrR1BW5/nKZNNW7k1O6OjEozvAajK JQdp3KBU9S8CmBjGrRpJ2qw= -----END PRIVATE KEY----- ================================================ FILE: examples/resource/path/to/client-public.crt ================================================ -----BEGIN CERTIFICATE----- MIIDdzCCAl+gAwIBAgIEfP3e8zANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQGEwJV UzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxDTALBgNVBAoT BFdTTzIxDTALBgNVBAsTBFdTTzIxEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0xNzEw MjQwNTQ3NThaFw0zNzEwMTkwNTQ3NThaMGQxCzAJBgNVBAYTAlVTMQswCQYDVQQI EwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzENMAsGA1UEChMEV1NPMjENMAsG A1UECxMEV1NPMjESMBAGA1UEAxMJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF AAOCAQ8AMIIBCgKCAQEAgVyi6fViVLiZKEnw59xzNi1lcYh6z9dZnug+F9gKqFIg mdcPe+qtS7gZc1jYTjWMCbx13sFLkZqNHeDUadpmtKo3TDduOl1sqM6cz3yXb6L3 4k/leh50mzIPNmaaXxd3vOQoK4OpkgO1n32mh6+tkp3sbHmfYqDQrkVK1tmYNtPJ ffSCLT+CuIhnJUJco7N0unax+ySZN67/AX++sJpqAhAIZJzrRi6ueN3RFCIxYDXS MvxrEmOdn4gOC0o1Ar9u5Bp9N52sqqGbN1x6jNKi3bfUj122Hu5e+Y9KOmfbchhQ il2P81cIi30VKgyDn5DeWEuDoYredk4+6qAZrxMw+wIDAQABozEwLzAOBgNVHQ8B Af8EBAMCBaAwHQYDVR0OBBYEFNmtrQ36j6tUGhKrfW9qWWE7KFzMMA0GCSqGSIb3 DQEBCwUAA4IBAQAv3yOwgbtOu76eJMl1BCcgTFgaMUBZoUjK9Un6HGjKEgYz/YWS ZFlY/qH5rT01DWQevUZB626d5ZNdzSBZRlpsxbf9IE/ursNHwHx9ua6fB7yHUCzC 1ZMp1lvBHABi7wcA+5nbV6zQ7HDmBXFhJfbgH1iVmA1KcvDeBPSJ/scRGasZ5q2W 3IenDNrfPIUhD74tFiCiqNJO91qD/LO+++3XeZzfPh8NRKkiPX7dB8WJ3YNBuQAv gRWTISpSSXLmqMb+7MPQVgecsepZdk8CwkRLxh3RKPJMjigmCgyvkSaoDMKAYC3i YjfUTiJ57UeqoSl0IaOFJ0wfZRFh+UytlDZa -----END CERTIFICATE----- ================================================ FILE: examples/resource/path/to/encryptedPrivate.key ================================================ -----BEGIN ENCRYPTED PRIVATE KEY----- MIIE6jAcBgoqhkiG9w0BDAEEMA4ECJ1AVEOFT+s1AgIFDASCBMiHG70PvakbuVha 9JBcXWzje1B30LItVdpiETbu5LNG3v0IIX7DmRQCBJxXkkCeZ3Bcw/A1cjkGIR0j 6P1AyUAlZBU9+GcCF2BcpyaMOxNVQuHUZA9//fnlfO6KMTdIg9b/MutH++bLSoh9 JkHivtgEQVEAu79NpTdKh9XklIWUBe5gU4OteBL0/feKgB6NFeYm7vNb8b486jSB oXWC7l8Y5VoU4grhDT9dL1PQqLQvIRWTnB1BhUE9OquWrRRaGE7WeWABzzFhHB4X DdHABqsNE9qeEGHrUBKfjfg6+8GIEjjJcpJt9wGBi4yHbUaao7xueu0aA9nVVo75 6PJOwcJqoPhwkDFpAYADLJTVxh8EqbaihSe3HVqjsF3au5nu338VbJu8VZ2rAgTv V2IxsV/BWrv8RIbt/EINz82uU0umbhpvUakMjZGD01NCvx6mshfKA3zIEac7apmc 6wwoqh42sEHk3i7H/xQhuS32ACzRDwXzXYf2f8CZVrSk7Ajc4w4mwY5YylCmn+P5 YK6fRCaJAN2J4CGvIut3LvjGX8pNuu9msFMrFjbITL/xeUzIrJoE1C9O2V1dQ5LU O4wVFGKHkFpBBzxxDQwRbYn66EbL4/dCwtIkj7kncz7Y+qYBi+Voe81RSL1mBtR/ nOQPIHNOLSUvIPA2aj4ufcdSrbXapMfrTOiO+EUuBOGE6FncaGm3hfKnPa1C4fuZ yokvpEMNOUs5eJjSC0tcP+zibTkiMHy2vhvrI3xme6oIFG7Nvxi2MxcWolUNmXlk UVGyJJurL9QhKfjGSIuEZgJd7/PrDK0gzEQBMS/10BUPRe1pE+05GEOvJBbhw1Bt 6b3LjbVtNnR/2/G3I9LlwRLac4IHlU/JMy58E3Uxim0/rJJ7uGGbXsxdwFS4968I /ekA1PcZgvNet60rMYxSXz6QPwYnvM94gFD6I5PgUhJJPeQZiw9kFLBqKsUjBrjY jFAcTVW6sWEo1CMsUC94gwvHvpOLtPHjik/iPAfHXeGQ9baTaArMAJiQO+0h9O3Y 0rXM8gEz4zAID3N4neVjjMda7C/2avAfJRoBxYAFMcuMNTta4mMmXOCs+M6x45GI w/KPAZbsRTcRWLjU5QguqP6eAxSghHITruW0HEqscT20K3cRWXXDEOr8ZqId90xY PQo5ZEvaJLGgxN4Q7CUPJJ1mKHruTmMm+UOxIuXRR2eaqsJDnLovmu4jGOSLVYV4 FFJ7PxD+tx9b8WS8tckS/Pjm2PcUAJz3I6/DFc3iKwoeCXBIG/djwWfabRcoGb0U Sq8tiqvCjT/LuubpQgKhkEnxWCBRR+Qns4quJJ2Kead/g8E19cUxolGPTW9jr2GF 1XWuGytK0PqUOIMsuJkhWN3dErFmftcfmfhqFFulOvE7eWr+ZToyFESmNK+MNnAL mmZFOt7zTNwooQkzpVohLtKPwe3k71dhhsK3gQBoscKQFnMQSmKLM7c4A6HPcq20 XFtRM30O5cSO2jLZ+sAcJS81uIrcPWWf8yY5lMZ1irHFri6jnwcv7Sbe3rqZJFer 9ckVz4rq399UHOyujNT2gjIaFnEwmHfM0yaZ0dYG2JU/jXdbqsTLKsHSOZsrkkL5 9BaHO8wfzVx/CnQfP6M= -----END ENCRYPTED PRIVATE KEY----- ================================================ FILE: examples/resource/path/to/private.key ================================================ -----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCBXKLp9WJUuJko SfDn3HM2LWVxiHrP11me6D4X2AqoUiCZ1w976q1LuBlzWNhONYwJvHXewUuRmo0d 4NRp2ma0qjdMN246XWyozpzPfJdvovfiT+V6HnSbMg82ZppfF3e85Cgrg6mSA7Wf faaHr62SnexseZ9ioNCuRUrW2Zg208l99IItP4K4iGclQlyjs3S6drH7JJk3rv8B f76wmmoCEAhknOtGLq543dEUIjFgNdIy/GsSY52fiA4LSjUCv27kGn03nayqoZs3 XHqM0qLdt9SPXbYe7l75j0o6Z9tyGFCKXY/zVwiLfRUqDIOfkN5YS4Ohit52Tj7q oBmvEzD7AgMBAAECggEAXM/F4u23OummmQ1T1kaIMpqnaalt06jCGAywYBMUsmca FMYDyfg5lVXkjKl1p8crTeD1AHjWawTjskgYnkmf3ocxXXF3mFBnIUX7o7HURLg7 +RcxoUgwiRiFaZZ7szX3JoLbfzzbcHNQ37kavccBVWwQsFMiU3Tlw+LbKwK6/row LYsQPx7gT4u7hViat4vQDTYcgyjvvFCiek4ndL6O9K49MxIMU678UXB6ia5iUevy vgEfcYkKQ5EQ38qS3ZwsubPvj4633jvAJRr/hJD8XINZC74kTXeV3BGH2LlpQOEq kWkOypwYNjnXtt1JO8+Iu6mEXKUoiIBPfGrJ3vDSQQKBgQDmYPc7kfYan/LHjJRv iE2CwbC26yVA6+BEPQv9z7jChO9Q6cUbGvM8EEVNpC9nmFogkslzJhz55HP84QZL u3ptU+D96ncq6zkBqxBfRnZG++D36+XRXIwzz3h+g1Nwrl0y0MFbwlkMm3ZqJdd6 pZz1FZGd6zvQftW8m7jPSKHuswKBgQCPv6czFOZR6bI+qCQdaORpe9JGoAduOD+4 YKl96s0eiAKhkGhFCrMd6GJwWRkpNcfwB+J9sMahORbfvwiYanI56h7Vi30DFPRb m1m8dLkr6z+8bxMxKJaMXIIjy3UDamgDr7QHInNUih2iGvtB8QqZ0aobsB2XIxZg qESTMcpYmQKBgHSwSqneraQgvgz7FLhFdtUzHDoacr0mfGqz7R37F99XDAyUy+SF ywvyRdgkwGodjhEPqH/tnyGn6GP+6nxzknhL0xtppkCT8kT5C4rmmsQrknChCL/5 u34GqUaTaDEb8FLrz/SVRRuQpvLvBey2dADjkuVFH//kLoig64P6iyLnAoGBAIlF g+2L78YZXVXoS1SqbjUtQUigWXgvzunLpQ/Rwb9+MsUGmgwUg6fz2s1eyGBKM3xM i0VsIsKjOezBCPxD6oDTyk4yvlbLE+7HE5KcBJikNmFD0RgIonu3e6+jA0MXweyD RW/qviflHRdInNgDzxPE3KVEMX26zAvRpGrMCWdBAoGAdQ5SvX+mAC3cKqoQ9Zal lSqWoyjfzP5EaVRG8dtoLxbznQGTTvtHXc65/MznX/L9qkWCS6Eb4HH5M3hFNY46 LNIzGQLznE1odwv7H5B8c0/m3DrKTxbh8bYcrR1BW5/nKZNNW7k1O6OjEozvAajK JQdp3KBU9S8CmBjGrRpJ2qw= -----END PRIVATE KEY----- ================================================ FILE: examples/resource/path/to/public.crt ================================================ -----BEGIN CERTIFICATE----- MIIDdzCCAl+gAwIBAgIEfP3e8zANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQGEwJV UzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxDTALBgNVBAoT BFdTTzIxDTALBgNVBAsTBFdTTzIxEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0xNzEw MjQwNTQ3NThaFw0zNzEwMTkwNTQ3NThaMGQxCzAJBgNVBAYTAlVTMQswCQYDVQQI EwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzENMAsGA1UEChMEV1NPMjENMAsG A1UECxMEV1NPMjESMBAGA1UEAxMJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF AAOCAQ8AMIIBCgKCAQEAgVyi6fViVLiZKEnw59xzNi1lcYh6z9dZnug+F9gKqFIg mdcPe+qtS7gZc1jYTjWMCbx13sFLkZqNHeDUadpmtKo3TDduOl1sqM6cz3yXb6L3 4k/leh50mzIPNmaaXxd3vOQoK4OpkgO1n32mh6+tkp3sbHmfYqDQrkVK1tmYNtPJ ffSCLT+CuIhnJUJco7N0unax+ySZN67/AX++sJpqAhAIZJzrRi6ueN3RFCIxYDXS MvxrEmOdn4gOC0o1Ar9u5Bp9N52sqqGbN1x6jNKi3bfUj122Hu5e+Y9KOmfbchhQ il2P81cIi30VKgyDn5DeWEuDoYredk4+6qAZrxMw+wIDAQABozEwLzAOBgNVHQ8B Af8EBAMCBaAwHQYDVR0OBBYEFNmtrQ36j6tUGhKrfW9qWWE7KFzMMA0GCSqGSIb3 DQEBCwUAA4IBAQAv3yOwgbtOu76eJMl1BCcgTFgaMUBZoUjK9Un6HGjKEgYz/YWS ZFlY/qH5rT01DWQevUZB626d5ZNdzSBZRlpsxbf9IE/ursNHwHx9ua6fB7yHUCzC 1ZMp1lvBHABi7wcA+5nbV6zQ7HDmBXFhJfbgH1iVmA1KcvDeBPSJ/scRGasZ5q2W 3IenDNrfPIUhD74tFiCiqNJO91qD/LO+++3XeZzfPh8NRKkiPX7dB8WJ3YNBuQAv gRWTISpSSXLmqMb+7MPQVgecsepZdk8CwkRLxh3RKPJMjigmCgyvkSaoDMKAYC3i YjfUTiJ57UeqoSl0IaOFJ0wfZRFh+UytlDZa -----END CERTIFICATE----- ================================================ FILE: examples/resource/path/to/server-private.key ================================================ -----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCBXKLp9WJUuJko SfDn3HM2LWVxiHrP11me6D4X2AqoUiCZ1w976q1LuBlzWNhONYwJvHXewUuRmo0d 4NRp2ma0qjdMN246XWyozpzPfJdvovfiT+V6HnSbMg82ZppfF3e85Cgrg6mSA7Wf faaHr62SnexseZ9ioNCuRUrW2Zg208l99IItP4K4iGclQlyjs3S6drH7JJk3rv8B f76wmmoCEAhknOtGLq543dEUIjFgNdIy/GsSY52fiA4LSjUCv27kGn03nayqoZs3 XHqM0qLdt9SPXbYe7l75j0o6Z9tyGFCKXY/zVwiLfRUqDIOfkN5YS4Ohit52Tj7q oBmvEzD7AgMBAAECggEAXM/F4u23OummmQ1T1kaIMpqnaalt06jCGAywYBMUsmca FMYDyfg5lVXkjKl1p8crTeD1AHjWawTjskgYnkmf3ocxXXF3mFBnIUX7o7HURLg7 +RcxoUgwiRiFaZZ7szX3JoLbfzzbcHNQ37kavccBVWwQsFMiU3Tlw+LbKwK6/row LYsQPx7gT4u7hViat4vQDTYcgyjvvFCiek4ndL6O9K49MxIMU678UXB6ia5iUevy vgEfcYkKQ5EQ38qS3ZwsubPvj4633jvAJRr/hJD8XINZC74kTXeV3BGH2LlpQOEq kWkOypwYNjnXtt1JO8+Iu6mEXKUoiIBPfGrJ3vDSQQKBgQDmYPc7kfYan/LHjJRv iE2CwbC26yVA6+BEPQv9z7jChO9Q6cUbGvM8EEVNpC9nmFogkslzJhz55HP84QZL u3ptU+D96ncq6zkBqxBfRnZG++D36+XRXIwzz3h+g1Nwrl0y0MFbwlkMm3ZqJdd6 pZz1FZGd6zvQftW8m7jPSKHuswKBgQCPv6czFOZR6bI+qCQdaORpe9JGoAduOD+4 YKl96s0eiAKhkGhFCrMd6GJwWRkpNcfwB+J9sMahORbfvwiYanI56h7Vi30DFPRb m1m8dLkr6z+8bxMxKJaMXIIjy3UDamgDr7QHInNUih2iGvtB8QqZ0aobsB2XIxZg qESTMcpYmQKBgHSwSqneraQgvgz7FLhFdtUzHDoacr0mfGqz7R37F99XDAyUy+SF ywvyRdgkwGodjhEPqH/tnyGn6GP+6nxzknhL0xtppkCT8kT5C4rmmsQrknChCL/5 u34GqUaTaDEb8FLrz/SVRRuQpvLvBey2dADjkuVFH//kLoig64P6iyLnAoGBAIlF g+2L78YZXVXoS1SqbjUtQUigWXgvzunLpQ/Rwb9+MsUGmgwUg6fz2s1eyGBKM3xM i0VsIsKjOezBCPxD6oDTyk4yvlbLE+7HE5KcBJikNmFD0RgIonu3e6+jA0MXweyD RW/qviflHRdInNgDzxPE3KVEMX26zAvRpGrMCWdBAoGAdQ5SvX+mAC3cKqoQ9Zal lSqWoyjfzP5EaVRG8dtoLxbznQGTTvtHXc65/MznX/L9qkWCS6Eb4HH5M3hFNY46 LNIzGQLznE1odwv7H5B8c0/m3DrKTxbh8bYcrR1BW5/nKZNNW7k1O6OjEozvAajK JQdp3KBU9S8CmBjGrRpJ2qw= -----END PRIVATE KEY----- ================================================ FILE: examples/resource/path/to/server-public.crt ================================================ -----BEGIN CERTIFICATE----- MIIDdzCCAl+gAwIBAgIEfP3e8zANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQGEwJV UzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxDTALBgNVBAoT BFdTTzIxDTALBgNVBAsTBFdTTzIxEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0xNzEw MjQwNTQ3NThaFw0zNzEwMTkwNTQ3NThaMGQxCzAJBgNVBAYTAlVTMQswCQYDVQQI EwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzENMAsGA1UEChMEV1NPMjENMAsG A1UECxMEV1NPMjESMBAGA1UEAxMJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF AAOCAQ8AMIIBCgKCAQEAgVyi6fViVLiZKEnw59xzNi1lcYh6z9dZnug+F9gKqFIg mdcPe+qtS7gZc1jYTjWMCbx13sFLkZqNHeDUadpmtKo3TDduOl1sqM6cz3yXb6L3 4k/leh50mzIPNmaaXxd3vOQoK4OpkgO1n32mh6+tkp3sbHmfYqDQrkVK1tmYNtPJ ffSCLT+CuIhnJUJco7N0unax+ySZN67/AX++sJpqAhAIZJzrRi6ueN3RFCIxYDXS MvxrEmOdn4gOC0o1Ar9u5Bp9N52sqqGbN1x6jNKi3bfUj122Hu5e+Y9KOmfbchhQ il2P81cIi30VKgyDn5DeWEuDoYredk4+6qAZrxMw+wIDAQABozEwLzAOBgNVHQ8B Af8EBAMCBaAwHQYDVR0OBBYEFNmtrQ36j6tUGhKrfW9qWWE7KFzMMA0GCSqGSIb3 DQEBCwUAA4IBAQAv3yOwgbtOu76eJMl1BCcgTFgaMUBZoUjK9Un6HGjKEgYz/YWS ZFlY/qH5rT01DWQevUZB626d5ZNdzSBZRlpsxbf9IE/ursNHwHx9ua6fB7yHUCzC 1ZMp1lvBHABi7wcA+5nbV6zQ7HDmBXFhJfbgH1iVmA1KcvDeBPSJ/scRGasZ5q2W 3IenDNrfPIUhD74tFiCiqNJO91qD/LO+++3XeZzfPh8NRKkiPX7dB8WJ3YNBuQAv gRWTISpSSXLmqMb+7MPQVgecsepZdk8CwkRLxh3RKPJMjigmCgyvkSaoDMKAYC3i YjfUTiJ57UeqoSl0IaOFJ0wfZRFh+UytlDZa -----END CERTIFICATE----- ================================================ FILE: examples/resource-method-typing/resource_method_typing.bal ================================================ import ballerina/http; type Args record {| decimal x; decimal y; |}; type Response record {| decimal result; |}; listener http:Listener ln = new (9090); service /calc on ln { // Resource method arguments can use user-defined types. // Annotations can be used to refine the mapping between the Ballerina-declared // type and wire format. resource function post add(Args args) returns Response { return {result: args.x + args.y}; } } ================================================ FILE: examples/resource-method-typing/resource_method_typing.client.out ================================================ $ curl http://localhost:9090/calc/add -H 'content-type: application/json' -d "{\"x\": 1.0, \"y\": 2.0}" {"result":3.0} ================================================ FILE: examples/resource-method-typing/resource_method_typing.md ================================================ # Resource method typing Resource method arguments can use user-defined types. The listener will use introspection to map the protocol format (typically JSON) to a user-defined type using `cloneWithType`. The return value, which is a subtype of anydata will be mapped from the user-defined type to the protocol format typically JSON, using `toJson`. The API description (e.g. OpenAPI) can be generated from the Ballerina service declaration. Annotations can be used to refine the mapping between the Ballerina-declared type and wire format. ::: code resource_method_typing.bal ::: Run the service using the `bal run` command. ::: out resource_method_typing.server.out ::: Run this cURL command to invoke the resource. ::: out resource_method_typing.client.out ::: ## Related links - [Casting JSON to user-defined type](/learn/by-example/casting-json-to-user-defined-type/) - [JSON type](/learn/by-example/json-type/) - [Service data binding](/learn/by-example/http-service-data-binding/) - [http module](https://lib.ballerina.io/ballerina/http) ================================================ FILE: examples/resource-method-typing/resource_method_typing.metatags ================================================ description: This BBE demonstrates how to accept JSON in rest API, return JSON in rest API, payload binding, data binding, access payload. keywords: ballerina, ballerina by example, bbe, resource, service, data binding, json, record, http ================================================ FILE: examples/resource-method-typing/resource_method_typing.server.out ================================================ $ bal run resource_method_typing.bal ================================================ FILE: examples/resource-methods/resource_methods.bal ================================================ import ballerina/http; // Service declaration specifies base path for the resource names. The base path is `/` in this example. service / on new http:Listener(8080) { // Resource method is associated with combination of accessor (`get`) and resource name (`hello`). // Accessors are determined by the network protocol. // In HTTP resources, function parameters come from query parameters. resource function get hello(string name) returns string { return "Hello, " + name; } } ================================================ FILE: examples/resource-methods/resource_methods.client.out ================================================ $ curl "localhost:8080/hello?name=Ballerina" Hello, Ballerina ================================================ FILE: examples/resource-methods/resource_methods.md ================================================ # Resource methods - Service objects use `remote` methods to expose services in procedural style: remote methods are named by verbs. - Service objects use `resource` methods to expose services in an RESTful style: resources are named by nouns. Resources are motivated by HTTP, but are general enough also to work for GraphQL. `resource` methods are a network-oriented generalization of OO getter/setter concept. ::: code resource_methods.bal ::: Run the service using the `bal run` command. ::: out resource_methods.server.out ::: Run this cURL command to invoke the resource. ::: out resource_methods.client.out ::: ================================================ FILE: examples/resource-methods/resource_methods.metatags ================================================ description: This BBE introduces the resource concept in Ballerina. keywords: ballerina, ballerina by example, bbe, resources, resource methods, HTTP, GraphQL ================================================ FILE: examples/resource-methods/resource_methods.server.out ================================================ $ bal run resource_methods.bal ================================================ FILE: examples/resource-path-parameters/resource_path_parameters.bal ================================================ import ballerina/http; service /demo on new http:Listener(8080) { // Here is how you can make path segments as parameters. resource function get greeting/hello/[string name]() returns string { return "Hello, " + name; } } ================================================ FILE: examples/resource-path-parameters/resource_path_parameters.client.out ================================================ $ curl "localhost:8080/demo/greeting/hello/Ballerina" Hello, Ballerina ================================================ FILE: examples/resource-path-parameters/resource_path_parameters.md ================================================ # Resource path parameters Path segments can be treated as parameters in Ballerina. ::: code resource_path_parameters.bal ::: Run the service using the `bal run` command. ::: out resource_path_parameters.server.out ::: Run this cURL command to invoke the resource. ::: out resource_path_parameters.client.out ::: ================================================ FILE: examples/resource-path-parameters/resource_path_parameters.metatags ================================================ description: This BBE introduces how you can specify resource path parameters. keywords: ballerina, ballerina by example, bbe, path parameters, resources ================================================ FILE: examples/resource-path-parameters/resource_path_parameters.server.out ================================================ $ bal run resource_path_parameters.bal ================================================ FILE: examples/rest-arguments/rest_arguments.bal ================================================ import ballerina/http; import ballerina/io; // A function to print the sum of two or more integers. function sum(int first, int second, int... others) { int sum = first + second; foreach int othVal in others { sum += othVal; } io:println(sum); } // A record with some HTTP client configuration values. type Configuration record {| string url; decimal timeout; http:HttpVersion httpVersion; |}; // A function that initializes an HTTP client using some configuration. function initializeHttpClient(string url, decimal timeout, http:HttpVersion httpVersion) { // Intialize the client just for demonstration. http:Client|error cl = new (url, {timeout, httpVersion}); if cl is error { io:println("Failed to initialize an HTTP client", cl); return; } io:println( string `Initialized client with URL: ${url}, timeout: ${timeout}, HTTP version: ${httpVersion}`); } public function main() { // Call the `sum` function using an array of length 4 as a rest argument. int[4] ints1 = [1, 2, 3, 4]; sum(...ints1); // Since the `sum` function has two required parameters, when providing only a list rest // argument, the length of the list should be guaranteed by the static type to be at // least 2. // Call the `sum` function using a tuple with at least two members. [int, int, int...] ints2 = [5, 6]; sum(...ints2); // A rest argument can be used along with positional arguments, // providing only some of the arguments. // Call the `sum` function with a rest argument after two positional arguments. sum(5, 6, ...ints1); // Call the `sum` function with a rest argument after one positonal argument. // Note how the rest argument provides arguments for both a required parameter // and the rest parameter. Since only one positional argument is provided, the list // type of the expression used in the rest argument should guarantee the presence of // at least one member in the list to provide an argument for the second required parameter. [int, int...] ints3 = [5, 6, 7]; sum(4, ...ints3); // Call the `initializeHttpClient` function using a record value in the rest argument // providing values for all the parameters. Configuration config1 = {httpVersion: http:HTTP_2_0, url: "http://localhost:8080", timeout: 60}; initializeHttpClient(...config1); // Call the `initializeHttpClient` function using a positional argument and a rest argument with // a record value. The positional argument provides the argument for the first parameter (`url`) and the // rest argument provides values for the other two parameters. record {| decimal timeout; http:HttpVersion httpVersion; |} config2 = {httpVersion: http:HTTP_1_1, timeout: 15}; initializeHttpClient("http://localhost:8080", ...config2); } ================================================ FILE: examples/rest-arguments/rest_arguments.md ================================================ # Rest arguments Ballerina allows you to call functions with rest arguments, with an expression of a mapping or list type, spreading the members of the mapping or the list as individual arguments to the function. If the type of the expression used in the rest argument is a list type, the rest argument is equivalent to specifying each member of the list value as a positional argument. If it is a mapping type, the rest argument is equivalent to specifying each field of the mapping value as a named argument, where the name and the value of the named argument come from the name and the value of the field. In either case, the static type of the expression must ensure that the equivalent positional and/or named arguments would be valid and that arguments are provided for all the required parameters. ::: code rest_arguments.bal ::: ::: out rest_arguments.out ::: ## Related links - [Functions](/learn/by-example/functions/) - [Provide function arguments by name](/learn/by-example/provide-function-arguments-by-name/) - [Included record parameters](/learn/by-example/included-record-parameters/) - [Aggregation](/learn/by-example/aggregation/) ================================================ FILE: examples/rest-arguments/rest_arguments.metatags ================================================ description: This BBE demonstrates calling functions with rest arguments in Ballerina. keywords: ballerina, ballerina by example, bbe, rest arguments, functions, function definition, parameters ================================================ FILE: examples/rest-arguments/rest_arguments.out ================================================ $ bal run rest_arguments.bal 10 11 21 22 Initialized client with URL: http://localhost:8080, timeout: 60, HTTP version: 2.0 Initialized client with URL: http://localhost:8080, timeout: 15, HTTP version: 1.1 ================================================ FILE: examples/rest-binding-pattern-in-error-binding-pattern/rest_binding_pattern_in_error_binding_pattern.bal ================================================ import ballerina/io; type SampleErrorData record {| int code; string reason; |}; type SampleError error; public function main() { // The detail mapping can be destructured using a rest parameter. // `details` is of type `map` having the `code` and `reason` fields. var error(message, ...details) = getSampleError(); io:println("Message: ", message); map detailsMap = details; io:println("Details: ", detailsMap); // Here, the `...filteredDetails` rest parameter contains only the detail fields // that are not matched. var error(_, code = code, ...filteredDetails) = getSampleError(); io:println("Code: ", code); io:println("Filtered Details: ", filteredDetails); map moreDetails; // The detail mapping can be destructured into a `map` typed variable // by using a rest parameter. error(_, ...moreDetails) = getSampleError(); io:println("All Details: ", moreDetails); } function getSampleError() returns SampleError { return error("Transaction Failure", error("Database Error"), code = 20, reason = "deadlock condition"); } ================================================ FILE: examples/rest-binding-pattern-in-error-binding-pattern/rest_binding_pattern_in_error_binding_pattern.md ================================================ # Rest binding pattern in error binding pattern You can use the rest binding pattern (`...r`) to bind the detail mappings that are not explicitly bound in the error binding pattern. The type of the rest binding will be a `map` holding the fields that have not been matched. ::: code rest_binding_pattern_in_error_binding_pattern.bal ::: ::: out rest_binding_pattern_in_error_binding_pattern.out ::: ## Related links - [Binding Patterns](/learn/by-example/binding-patterns/) - [Typed Binding Pattern](/learn/by-example/typed-binding-pattern/) - [Error Binding Pattern](/learn/by-example/error-binding-pattern/) ================================================ FILE: examples/rest-binding-pattern-in-error-binding-pattern/rest_binding_pattern_in_error_binding_pattern.metatags ================================================ description: This BBE demonstrates the rest binding pattern in the error binding pattern, rest parameter in error binding, rest binding, error binding pattern, error typed binding, and error destructuring with binding patterns. keywords: ballerina, ballerina by example, bbe, binding pattern, rest binding, rest parameter, error binding pattern, error binding, error typed binding, error binding, error destructuring ================================================ FILE: examples/rest-binding-pattern-in-error-binding-pattern/rest_binding_pattern_in_error_binding_pattern.out ================================================ $ bal run rest_binding_pattern_in_error_binding_pattern.bal Message: Transaction Failure Details: {"code":20,"reason":"deadlock condition"} Code: 20 Filtered Details: {"reason":"deadlock condition"} All Details: {"code":20,"reason":"deadlock condition"} ================================================ FILE: examples/rest-binding-pattern-in-list-binding-pattern/rest_binding_pattern_in_list_binding_pattern.bal ================================================ import ballerina/io; public function main() { float[4] [first, second, ...others] = getScores(); // The first two values returned by `getScores()` will be bound to the // variables `first` and `second` which will be of `float` type. io:println(first); io:println(second); // The rest of the values will be bound to `others`, which will be of `float[]` type. io:println(others); } function getScores() returns float[4] { return [1.2, 2.3, 3.4, 4.5]; } ================================================ FILE: examples/rest-binding-pattern-in-list-binding-pattern/rest_binding_pattern_in_list_binding_pattern.md ================================================ # Rest binding pattern in list binding pattern If the number of binding patterns is less than the members in the list value, the rest binding pattern (`...r`) can be used to create a new list value consisting of the members that are not bound. ::: code rest_binding_pattern_in_list_binding_pattern.bal ::: ::: out rest_binding_pattern_in_list_binding_pattern.out ::: ## Related links - [List binding pattern](/learn/by-example/list-binding-pattern/) - [Binding patterns](/learn/by-example/binding-patterns/) - [Rest type in tuples](/learn/by-example/rest-type-in-tuples/) ================================================ FILE: examples/rest-binding-pattern-in-list-binding-pattern/rest_binding_pattern_in_list_binding_pattern.metatags ================================================ description: This BBE introduces the use of rest binding pattern in a list. keywords: ballerina, ballerina by example, bbe, binding pattern, rest binding pattern ================================================ FILE: examples/rest-binding-pattern-in-list-binding-pattern/rest_binding_pattern_in_list_binding_pattern.out ================================================ $ bal run rest_binding_pattern_in_list_binding_pattern.bal 1.2 2.3 [3.4,4.5] ================================================ FILE: examples/rest-binding-pattern-in-mapping-binding-pattern/rest_binding_pattern_in_mapping_binding_pattern.bal ================================================ import ballerina/io; type Person record { int id; string fname; string lname; }; public function main() { // The value of the `id` field is ignored using `_`. // The value of the `fname` field is matched with the `firstName` variable. // `...otherDetails` is a rest parameter. The remaining fields that have not been matched // are matched with `...otherDetails`. Person {id: _, fname: firstName, ...otherDetails} = getPerson(); io:println(firstName); // The type of `otherDetails` is a `record` holding the remaining fields. record {|string lname; anydata...;|} details = otherDetails; io:println(details); string fname; record {|string lname; anydata...;|} otherInfo; // The values of the fields in the destructed record are assigned to the variable references. // The value of the `fname` field is assigned to the `fname` variable. // The remaining field values that have not been matched are assigned to `...otherInfo`. {fname, ...otherInfo} = getPerson(); io:println(fname); io:println(otherInfo); } function getPerson() returns Person { Person person = {id: 1001, fname: "Anne", lname: "Frank", "age": 24, "country": "UK"}; return person; } ================================================ FILE: examples/rest-binding-pattern-in-mapping-binding-pattern/rest_binding_pattern_in_mapping_binding_pattern.md ================================================ # Rest binding pattern in mapping binding pattern You can use the rest binding pattern (`...r`) to bind the fields that are not explicitly bound in the mapping binding pattern. The type of the rest binding will be a `record` holding the fields that have not been matched. ::: code rest_binding_pattern_in_mapping_binding_pattern.bal ::: ::: out rest_binding_pattern_in_mapping_binding_pattern.out ::: ## Related links - [Binding patterns](/learn/by-example/binding-patterns/) - [Typed binding pattern](/learn/by-example/typed-binding-pattern/) - [Mapping binding pattern](/learn/by-example/mapping-binding-pattern/) ================================================ FILE: examples/rest-binding-pattern-in-mapping-binding-pattern/rest_binding_pattern_in_mapping_binding_pattern.metatags ================================================ description: This BBE demonstrates the rest binding pattern, rest binding pattern in mapping binding, rest binding pattern in record typed binding, record destructuring with binding patterns, and JSON destructuring. keywords: ballerina, ballerina by example, bbe, binding pattern, rest binding, mapping binding pattern, mapping binding, record typed binding, record binding, record destructuring, json destructuring ================================================ FILE: examples/rest-binding-pattern-in-mapping-binding-pattern/rest_binding_pattern_in_mapping_binding_pattern.out ================================================ $ bal rest_binding_pattern_in_mapping_binding_pattern.bal Anne {"lname":"Frank","age":24,"country":"UK"} Anne {"lname":"Frank","id":1001,"age":24,"country":"UK"} ================================================ FILE: examples/rest-parameters/rest_parameters.bal ================================================ import ballerina/io; function foo(int n, string... s) { io:println(n); io:println(s[0]); io:println(s[1]); io:println(s[2]); io:println(s); io:println(s is string[]); } public function main() { // The `s` parameter will be `["x", "y", "z"]`. foo(1, "x", "y", "z"); } ================================================ FILE: examples/rest-parameters/rest_parameters.md ================================================ # Rest Parameters Ballerina supports rest parameters. There can not be another parameter after a rest parameter. If a function has a rest parameter, that will be initialized to a newly created list with the remaining arguments in the function. The inherent type of this list is `T[]` in which `T` is the type of the rest parameter. ::: code rest_parameters.bal ::: ::: out rest_parameters.out ::: ================================================ FILE: examples/rest-parameters/rest_parameters.metatags ================================================ description: This BBE demonstrates rest parameters in Ballerina. keywords: ballerina, ballerina by example, bbe, rest parameters, rest params ================================================ FILE: examples/rest-parameters/rest_parameters.out ================================================ 1 x y z ["x","y","z"] true ================================================ FILE: examples/rest-type-in-tuples/rest_type_in_tuples.bal ================================================ import ballerina/io; public function main() { // declare a tuple with zero or more `int` members after the first member of type `string`. [string, int...] scoreList = ["John", 55, 43, 65, 65]; io:println(scoreList); [string, int...] secondScoreList = ["Amy"]; io:println(secondScoreList); // [T...] is equivalent to array T[]. [int...] scores = []; io:println(scores); scores = [23, 53]; io:println(scores); // New members can be pushed to a tuple with rest type by using `array:push()` method scores.push(43); io:println(scores); } ================================================ FILE: examples/rest-type-in-tuples/rest_type_in_tuples.md ================================================ # Rest type in tuples A Tuple type descriptor can optionally contain a tuple rest descriptor. It can be described as `[R...]`. This implies that the tuple can contain zero or more members after the `nth` member where the type of those members are `R`. Rest type descriptor should be the last member type descriptor in the tuple. Tuples are not open by default. ::: code rest_type_in_tuples.bal ::: ::: out rest_type_in_tuples.out ::: ## Related links - [Tuples](/learn/by-example/tuples) - [Arrays](/learn/by-example/arrays) - [Manipulating an array `(lang.array)`](https://lib.ballerina.io/ballerina/lang.array) - [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/rest-type-in-tuples/rest_type_in_tuples.metatags ================================================ description: This BBE demonstrates how to create variable length tuple, create tuple with rest type descriptor. keywords: ballerina, ballerina by example, bbe, rest type, tuple rest type, collection, length, size, list, rest, list binding pattern, push ================================================ FILE: examples/rest-type-in-tuples/rest_type_in_tuples.out ================================================ $ bal run rest_type_in_tuples.bal ["John",55,43,65,65] ["Amy"] [] [23,53] [23,53,43] ================================================ FILE: examples/retry-transaction-statement/retry_transaction_statement.bal ================================================ import ballerina/io; public function main() returns error? { // Short for `retry(3)`. // If any of the `doStage1` and `doStage2` returns `error:Retriable`, the program will retry execution // until execution succeeds without an `error:Retriable` error. // By default, it will retry 3 times with the `DefaultRetryManager`. retry transaction { check doStage1(); check doStage2(); check commit; } return; } function doStage1() returns error? { io:println("Stage1 completed"); return; } function doStage2() returns error? { // Returns an `error:Retriable` error for retrying. // To support custom errors, a custom implementation of the `RetryManager` is required. return error 'error:Retriable("Stage2 failed"); } ================================================ FILE: examples/retry-transaction-statement/retry_transaction_statement.md ================================================ # Retry transaction statement Transactional errors are often transient: retrying will fix them. This works by creating a RetryManager object `r`, before executing the transaction. If the block fails with error `e`, it calls `r.shouldRetry(e)`. If that returns `true`, then it executes the block again. `retry` has an optional type parameter giving class of `RetryManager` to create, and optional arguments to new `DefaultRetryManager` tries `n` times. `retry` can be used without `transaction`. ::: code retry_transaction_statement.bal ::: ::: out retry_transaction_statement.out ::: ================================================ FILE: examples/retry-transaction-statement/retry_transaction_statement.metatags ================================================ description: This BBE demonstrates retrying a transaction in Ballerina. keywords: ballerina, ballerina by example, bbe, retry, retry transaction, retry manager ================================================ FILE: examples/retry-transaction-statement/retry_transaction_statement.out ================================================ $ bal run retry_transaction_statement.bal Stage1 completed Stage1 completed Stage1 completed Stage1 completed error: Stage2 failed ================================================ FILE: examples/rollback/rollback.bal ================================================ import ballerina/io; // Defines the `Update` record type. type Update record { int updateIndex; int stockMnt; }; public function main() returns error? { // Creates an array of `Update` records. Update[] updates = [ {updateIndex: 0, stockMnt: 2000}, {updateIndex: 1, stockMnt: -1000}, {updateIndex: 2, stockMnt: 1500}, {updateIndex: 3, stockMnt: -1000}, {updateIndex: 4, stockMnt: -2000} ]; // This transfer will be rolled back. io:println(transfer(updates)); // Creates an array of employee salaries. int[] salaryList = [100, 200, 300, 100]; // This salary increment will be rolled back. check incrementSalary(salaryList); int[] salaryList2 = [100, 200, 100, 100]; // This salary increment will be successful. check incrementSalary(salaryList2); } function transfer(Update[] updates) returns error? { transaction { // Inside the transaction, call `doUpdate` on each `update` record. foreach var u in updates { // If an error is returned, the `transfer` function returns with // that error and the transaction is rolled back. check doUpdate(u); } // `commit` will not be called because of an implicit rollback. check commit; } return; } function doUpdate(Update u) returns error? { // If the stock amount is less than `-1500`, an error is returned. if (u.stockMnt < -1500) { return error("Not enough stocks: ", stockIndex = u.updateIndex); } return; } function incrementSalary(int[] salaryList) returns error? { transaction { foreach int index in 0 ..< salaryList.length() { salaryList[index] += 100; } // If the new total salary exceeds `1000`, then, the rollback statement performs // rollback on the transaction. int salarySum = int:sum(...salaryList); if (salarySum > 1000) { io:println("Budget exceeded"); rollback; } else { io:println("Salary increment successful"); check commit; } } } ================================================ FILE: examples/rollback/rollback.md ================================================ # Rollback If there is a fail or panic in the execution of the block, then the transaction is rolled back. Transaction statement can also contain a rollback statement. Every possible exit from a transaction block must be either `commit`, `rollback`, fail exit (e.g., from `check`), or panic exit. Rollback does not automatically restore Ballerina variables to values before the transaction. ::: code rollback.bal ::: ::: out rollback.out ::: ================================================ FILE: examples/rollback/rollback.metatags ================================================ description: This BBE demonstrates `rollback` operation in Ballerina. keywords: ballerina, ballerina by example, bbe, rollback, rollback transaction ================================================ FILE: examples/rollback/rollback.out ================================================ $ bal run rollback.bal error("Not enough stocks: ",stockIndex=4) Budget exceeded Salary increment successful ================================================ FILE: examples/run-strands-safely-on-separate-threads/run_strands_safely_on_separate_threads.bal ================================================ import ballerina/io; final int[] & readonly intArr = [4, 2, 10, 8, 6]; isolated function f1(int[] arr) returns int[] { return arr.sort(); } function f2(int[] arr) returns int[] { return arr.sort(); } isolated function f3() { // An isolated `start` action is allowed in an isolated function. // Here, the strand created by the `start` action will run on a separate thread. future _ = start f1([4, 2, 8, 6]); } isolated function f4() { // A named worker is allowed in an isolated function. // Here, the strand created by a named worker can run on a separate thread // if the body of the worker satisfies the requirements for an isolated function. worker A { int[] _ = f1(intArr); } } function f5() { future _ = start f1([4, 2, 8, 6]); } function f6() { worker A { int[] _ = f2(intArr); } } public function main() { // `f2` is inferred to be isolated. io:println(f2 is isolated function (int[]) returns int[]); // `f5` is inferred to be isolated. io:println(f5 is isolated function ()); // `f6` is inferred to be isolated. io:println(f6 is isolated function ()); } ================================================ FILE: examples/run-strands-safely-on-separate-threads/run_strands_safely_on_separate_threads.md ================================================ # Run strands safely on a separate thread based on isolation The isolated feature in Ballerina supports identifying cases in which strands created by a start action, or a named worker can be run safely on separate threads. A `start` action is isolated if the function or method it calls has a type that is isolated and the expression for every argument is an isolated expression. The object whose method is called by the method call or remote method call is treated as an argument. An isolated `start` action is allowed in an isolated function and the strand created by the `start` action will run on a separate thread from the current thread. The strand created by a named worker can run on a separate thread from the default worker if the body of the worker satisfies the requirements for an isolated function. ::: code run_strands_safely_on_separate_threads.bal ::: ::: out run_strands_safely_on_separate_threads.out ::: ================================================ FILE: examples/run-strands-safely-on-separate-threads/run_strands_safely_on_separate_threads.metatags ================================================ description: This BBE demonstrates how to run strands safely on a separate thread based on isolation. keywords: ballerina, ballerina by example, bbe, inferring isolated, strand ================================================ FILE: examples/run-strands-safely-on-separate-threads/run_strands_safely_on_separate_threads.out ================================================ $ bal run_strands_safely_on_separate_threads.bal true true true ================================================ FILE: examples/security-crypto/security_crypto.bal ================================================ import ballerina/crypto; import ballerina/io; import ballerina/random; function hash() returns error? { // Input value for hash operations. string value = "Hello Ballerina!"; byte[] input = value.toBytes(); // Hashing input value using the MD5 hashing algorithm, and printing the hash value using the Hex encoding. // For details, see https://lib.ballerina.io/ballerina/crypto/latest#hashMd5 byte[] output = crypto:hashMd5(input); io:println("Hex encoded hash with MD5: " + output.toBase16()); // Hashing the input value using the SHA1 hashing algorithm, and printing the hash value using the Base64 encoding. output = crypto:hashSha1(input); io:println("Base64 encoded hash with SHA1: " + output.toBase64()); // Hashing the input value using the SHA256 hashing algorithm, and printing the hash value using the Hex encoding. output = crypto:hashSha256(input); io:println("Hex encoded hash with SHA256: " + output.toBase16()); // Hashing the input value using the SHA384 hashing algorithm, and printing the hash value using the Base64 encoding. output = crypto:hashSha384(input); io:println("Base64 encoded hash with SHA384: " + output.toBase64()); // Hashing the input value using the SHA512 hashing algorithm, and printing the hash value using the Hex encoding. output = crypto:hashSha512(input); io:println("Hex encoded hash with SHA512: " + output.toBase16()); // The Hex-encoded CRC32B checksum generation for the input value. io:println("CRC32B for text: " + crypto:crc32b(input)); } function hmac() returns error? { // Input value for HMAC operations. string value = "Hello Ballerina!"; byte[] input = value.toBytes(); // The key used for the HMAC generation. string secret = "somesecret"; byte[] key = secret.toBytes(); // HMAC generation for the input value using the MD5 hashing algorithm, and printing the HMAC value using the Hex encoding. byte[] output = check crypto:hmacMd5(input, key); io:println("Hex encoded HMAC with MD5: " + output.toBase16()); // HMAC generation for input the value using the SHA1 hashing algorithm, and printing the HMAC value using the Base64 encoding. output = check crypto:hmacSha1(input, key); io:println("Base64 encoded HMAC with SHA1: " + output.toBase64()); // HMAC generation for the input value using the SHA256 hashing algorithm, and printing the HMAC value using the Hex encoding. output = check crypto:hmacSha256(input, key); io:println("Hex encoded HMAC with SHA256: " + output.toBase16()); // HMAC generation for the input value using the SHA384 hashing algorithm, and printing the HMAC value using the Base64 encoding. output = check crypto:hmacSha384(input, key); io:println("Base64 encoded HMAC with SHA384: " + output.toBase64()); // HMAC generation for the input value using the SHA512 hashing algorithm, and printing the HMAC value using the Hex encoding. output = check crypto:hmacSha512(input, key); io:println("Hex encoded HMAC with SHA512: " + output.toBase16()); } function decodePrivateKey() returns crypto:PrivateKey|error { // Obtaining the reference to an RSA private key by a key file. string keyFile = "../resource/path/to/private.key"; crypto:PrivateKey privateKey = check crypto:decodeRsaPrivateKeyFromKeyFile(keyFile); // Obtaining the reference to an RSA private key by an encrypted key file. string encryptedKeyFile = "../resource/path/to/encryptedPrivate.key"; privateKey = check crypto:decodeRsaPrivateKeyFromKeyFile(encryptedKeyFile, "ballerina"); // Obtaining the reference to an RSA private key stored within a PKCS#12 or PFX format archive file. crypto:KeyStore keyStore = { path: "../resource/path/to/ballerinaKeystore.p12", password: "ballerina" }; privateKey = check crypto:decodeRsaPrivateKeyFromKeyStore(keyStore, "ballerina", "ballerina"); return privateKey; } function decodePublicKey() returns crypto:PublicKey|error { // Obtaining the reference to an RSA public key by a cert file. string certFile = "../resource/path/to/public.crt"; crypto:PublicKey publicKey = check crypto:decodeRsaPublicKeyFromCertFile(certFile); // Obtaining reference to a RSA public key stored within a PKCS#12 or PFX format archive file. crypto:TrustStore trustStore = { path: "../resource/path/to/ballerinaTruststore.p12", password: "ballerina" }; publicKey = check crypto:decodeRsaPublicKeyFromTrustStore(trustStore, "ballerina"); return publicKey; } function sign() returns error? { // Input value for the `sign` operations. string value = "Hello Ballerina!"; byte[] input = value.toBytes(); // Private and public keys for the `sign` and `verify` operations. crypto:PrivateKey privateKey = check decodePrivateKey(); crypto:PublicKey publicKey = check decodePublicKey(); // Signing the input value using the RSA-MD5 signature algorithms, and printing the signature value using the Hex encoding. byte[] output = check crypto:signRsaMd5(input, privateKey); io:println("Hex encoded RSA-MD5 signature: " + output.toBase16()); boolean verified = check crypto:verifyRsaMd5Signature(input, output, publicKey); io:println("RSA-MD5 signature verified: " + verified.toString()); // Signing the input value using the RSA-MD5 signature algorithms, and printing the signature value using the Base64 encoding. output = check crypto:signRsaSha1(input, privateKey); io:println("Base64 encoded RSA-SHA1 signature: " + output.toBase64()); verified = check crypto:verifyRsaSha1Signature(input, output, publicKey); io:println("RSA-SHA1 signature verified: " + verified.toString()); // Signing the input value using the RSA-MD5 signature algorithms, and printing the signature value using the Hex encoding. output = check crypto:signRsaSha256(input, privateKey); io:println("Hex encoded RSA-SHA256 signature: " + output.toBase16()); verified = check crypto:verifyRsaSha256Signature(input, output, publicKey); io:println("RSA-SHA256 signature verified: " + verified.toString()); // Signing the input value using the RSA-MD5 signature algorithms, and printing the signature value using the Base64 encoding. output = check crypto:signRsaSha384(input, privateKey); io:println("Base64 encoded RSA-SHA384 signature: " + output.toBase64()); verified = check crypto:verifyRsaSha384Signature(input, output, publicKey); io:println("RSA-SHA384 signature verified: " + verified.toString()); // Signing the input value using the RSA-MD5 signature algorithms, and printing the signature value using the Hex encoding. output = check crypto:signRsaSha512(input, privateKey); io:println("Hex encoded RSA-SHA512 signature: " + output.toBase16()); verified = check crypto:verifyRsaSha512Signature(input, output, publicKey); io:println("RSA-SHA512 signature verified: " + verified.toString()); } function encrypt() returns error? { // Input value for the `encrypt` operations. string value = "Hello Ballerina!"; byte[] input = value.toBytes(); // Private and public keys for the `encrypt` and `decrypt` operations. crypto:PrivateKey privateKey = check decodePrivateKey(); crypto:PublicKey publicKey = check decodePublicKey(); // Encrypts and decrypts an input value using the `RSA ECB PKCS1` padding. byte[] output = check crypto:encryptRsaEcb(input, publicKey); output = check crypto:decryptRsaEcb(output, privateKey); io:println("RSA ECB PKCS1 decrypted value: " + check string:fromBytes(output)); // Encrypts and decrypts an input value using the `RSA ECB OAEPwithSHA512andMGF1` padding. output = check crypto:encryptRsaEcb(input, publicKey, crypto:OAEPwithSHA512andMGF1); output = check crypto:decryptRsaEcb(output, privateKey, crypto:OAEPwithSHA512andMGF1); io:println("RSA ECB OAEPwithSHA512andMGF1 decrypted value: " + check string:fromBytes(output)); // Randomly generates a 128 bit key for the AES encryption. byte[16] aesKey = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; foreach var i in 0 ... 15 { aesKey[i] = (check random:createIntInRange(0, 255)); } // Randomly generates a 128 bit IV for the AES encryption. byte[16] iv = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; foreach var i in 0 ... 15 { iv[i] = (check random:createIntInRange(0, 255)); } // Encrypts and decrypts an input value using the `AES CBC PKCS5` padding. output = check crypto:encryptAesCbc(input, aesKey, iv); output = check crypto:decryptAesCbc(output, aesKey, iv); io:println("AES CBC PKCS5 decrypted value: " + check string:fromBytes(output)); // Encrypts and decrypts an input value using the `AES CBC` without padding. output = check crypto:encryptAesCbc(input, aesKey, iv, crypto:NONE); output = check crypto:decryptAesCbc(output, aesKey, iv, crypto:NONE); io:println("AES CBC no padding decrypted value: " + check string:fromBytes(output)); // Encrypts and decrypts an input value using the `AES GCM PKCS5` padding. output = check crypto:encryptAesGcm(input, aesKey, iv); output = check crypto:decryptAesGcm(output, aesKey, iv); io:println("AES GCM PKCS5 decrypted value: " + check string:fromBytes(output)); // Encrypts and decrypts an input value using the `AES GCM` without padding. output = check crypto:encryptAesGcm(input, aesKey, iv, crypto:NONE); output = check crypto:decryptAesGcm(output, aesKey, iv, crypto:NONE); io:println("AES GCM no padding decrypted value: " + check string:fromBytes(output)); // Encrypts and decrypts an input value using the `AES ECB PKCS5 padding`. output = check crypto:encryptAesEcb(input, aesKey); output = check crypto:decryptAesEcb(output, aesKey); io:println("AES ECB PKCS5 decrypted value: " + check string:fromBytes(output)); // Encrypts and decrypts an input value using the `AES ECB` without padding. output = check crypto:encryptAesEcb(input, aesKey, crypto:NONE); output = check crypto:decryptAesEcb(output, aesKey, crypto:NONE); io:println("AES ECB no padding decrypted value: " + check string:fromBytes(output)); } public function main() returns error? { check hash(); check hmac(); check sign(); check encrypt(); } ================================================ FILE: examples/security-crypto/security_crypto.md ================================================ # Cryptographic operations The `crypto` stdlib provides functions usable to perform different cryptographic operations such as hashing, HMAC generation, checksum generation, encryption, decryption, digitally signing data and verifying digitally signed data. For more information on the underlying module, see the [`crypto` module](https://lib.ballerina.io/ballerina/crypto/latest/). >**Tip:** You may need to change the certificate file path, private key file path, and trusted certificate file path in the code below. ::: code security_crypto.bal ::: Run the program by executing the command below. ::: out security_crypto.out ::: ## Related links - [Binary data](/learn/by-example/binary-data/) ================================================ FILE: examples/security-crypto/security_crypto.metatags ================================================ description: BBE on how to perform different cryptographic operations in Ballerina such as hashing, HMAC generation, checksum generation, encryption, decryption, digitally signing data and verifying digitally signed data. keywords: ballerina, ballerina by example, bbe, crypto, hash, hmac, sign, encrypt decrypt ================================================ FILE: examples/security-crypto/security_crypto.out ================================================ $ bal run security_crypto.bal Hex encoded hash with MD5: 0605402ee16d8e96511a58ff105bc24a Base64 encoded hash with SHA1: /8fwbGIevBvv2Nl3gEL9DtWas+Q= Hex encoded hash with SHA256: a984a643c350b17f0738bac0fef17f2cd91d91e04596351d0af 670c79adc12d5 Base64 encoded hash with SHA384: lselzItgAZHQmqNbkf/s2aRjBSd93O3ayc0PB0Dxk6AEo1s4 4zyTz/Qp0FJO1n6b Hex encoded hash with SHA512: a6f0770f1582f49396a97fbd5973ac22a3a578ac6a991786427 dfec17dbd984d8d6289771ac6f44176160a3dcd59f4a8c6b3ab97bef0caa5c67a3fac78c8e946 CRC32B for text: db9230c5 Hex encoded HMAC with MD5: b69fa2cc698e0923a7eea9d8f2b156fe Base64 encoded HMAC with SHA1: AkWFajkb/gml703Zf4pPgxrjam4= Hex encoded HMAC with SHA256: 13a3369b8ba326fd311d4500b06a5214a02ed2a033917108f6b 9af58b7ede381 Base64 encoded HMAC with SHA384: 0AjKoWLhNPgdobGTPJs0PdkA0W9wkJtzUvXigzC1ZmXDJJsx p4icks4JrPiwHGm6 Hex encoded HMAC with SHA512: 27588ad08e772a6bba5fca5f45cf467819c8de69a70a42be6fe 3eb09ceb3bfeb8b2976bda8ea5c10dcfa2294b12cb0b50b22a06309bada98af21857904a03205 Hex encoded RSA-MD5 signature: 2cfd121e4ff2409d1b2482ebbf37d0c035884d6d858e307e44 60b092d79cb20abb624a0dfae76b73b1fc85447be3060a36b318813f0115b1919e5efa7a7f9b1 173ec869f56fd9448d99770e1565db1c69a04fd0075fa0e33423a7e829a8b9c25a4dd2c68f3ee e021c0c4ff27979750b395384e280afd87af5274c8d2d99ad4438d9bfc9b2c5a2814212ba29ce 6ff70cbe30a5c23f86b0330e143c4d8813ff10092cd313c6861706d37df5f4bb4e9fc72354975 ee1786cf24c79b9edfa909968f198c4da37464f3d214a68fb39956717e92d667bb5a9a7f5986b a055d431813d4053a028873499f98c94fd6b5c6fd5aefad432669f957ab4ce9e91c5e77b36ec0 RSA-MD5 signature verified: true Base64 encoded RSA-SHA1 signature: bYMHKKVkjbOUp9ly3AdW9/euxF94krkkF9SDk2FfbVEc0m qpGIxVoZlPiFxszurZF1YPcqOSeOehDkaHXUMfQkTjBq7XlcePtkywy0fChqw8/SWqZR8nbYv97tt 8+MVTkymbm26syQLwYNLiGp/EsN6X+fJpkdakwHE+TrdE+rEFrNysGyAm1DWwc4c+l7MEmSYMUnh/ GWPY5r2knOOdDA3mr+XyrsxzFRWZgO7ebVvEQfq9XkRp8kdiGVgpLS5au0jKj3EpbCdS1prFgy3gr kuSJTTUQCwgPo7WSjWbuehFGni7rbas8HIqNlyWF0qUyznJ3eqbUwZ95QqOoVWZoQ== RSA-SHA1 signature verified: true Hex encoded RSA-SHA256 signature: 215c6ea96c9e82348430c6bb02e715560b4fbd3afcf24fb eb41ff12d4d68a797d61c4d6f822807688e4dc604e212b3cc7ac563b3cbe4e5690e2aebaf4e3d f35c19d4b0f7043f50501f390634303577053b029d495104c0e98bc887f0be744ef6f726f7192 01907ad4e86cef82eb030b60c384f7034a85159081e598e197bb8904a9123f39d190796dc7fd9 46157547c10523999b8fa956d4119dbfe3c1435911c0585cf3c537964516706772e87f2470557 40cc4867ac6b99d7bf699fce1b59956c7f55368c8c88c9d47e51ef120ed3f27c3e555691a6971 42c78cbd72c23b81b43fa5ab67164a35f8e8c6bf1da187d3feb866add13f1fb9576a2f7887535 311 RSA-SHA256 signature verified: true Base64 encoded RSA-SHA384 signature: BjQ40dffGiRQ4zo1s+ld+zKhJL21RbO5sW3L2+4xmonU t126u9D4/FZ2sM1QGGamj8btB9otiYmWr9sFm4fTs1EX6vrxcCGCAiDdkMxiRs7kShaz2x/BjJQ7c Od9OY+amwo7DQ/FAk9mNOt4lFUpjc9WyEW9F1PEJRXZQvMmVabDu8lp/Fh02lmEquG15DT5qT0jRx RJiS8CNa+97cMZdOmF2KeADfRbNJSz70mZ76MrsNxYIXYIiJzJBQod0efQr0Sr/HDn4JDVph9rpDM 3p8m94TyXvSOwxwxzZWRLEwB0ANdfDmbrW4bOpxfZZFmy1hltqNJQ9G0BcKOHsZDj6Q== RSA-SHA384 signature verified: true Hex encoded RSA-SHA512 signature: 15428fdc7b26d18b1f3eae4569399ae6ebfd430c8f073bf 2fa77ebfe1ad5645640374ea4a4aeadd252af3a198e55e69ad2a910e28470d9b54748887de06a 5c3ed7ab12399a404359332553e051e8ae0f3ef741faa15a21ad17a9c235e5f91d567bcca0e5a 6117689dccada4a33ee897514f7a8a32f12dac0087f5dcbb094c93c792f672e1685618ac5d93a a9d30f6d8e306145ef2d1b9cfdc04d6c61b43376089a78471e8e03d97ee3b57e1b734a23f4436 6a99234a0abeb1d36d01c474833b4c2beaf430dae06ab95a1c951645fb1e0a5e7b9eed44d40e3 5036f2cd2764df6cc04fe1248e1bb772a53c8201a974109333a318ce57930494d4cb5e41d0dc8 f1c RSA-SHA512 signature verified: true RSA ECB PKCS1 decrypted value: Hello Ballerina! RSA ECB OAEPwithSHA512andMGF1 decrypted value: Hello Ballerina! AES CBC PKCS5 decrypted value: Hello Ballerina! AES CBC no padding decrypted value: Hello Ballerina! AES GCM PKCS5 decrypted value: Hello Ballerina! AES GCM no padding decrypted value: Hello Ballerina! AES ECB PKCS5 decrypted value: Hello Ballerina! AES ECB no padding decrypted value: Hello Ballerina! ================================================ FILE: examples/security-crypto/tests/security_crypto_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... val) { outputs.push(val.reduce(function (any a, any b) returns string => a.toString() + b.toString(), "").toString()); } @test:Config {} function testFunc() returns error? { test:when(mock_printLn).call("mockPrint"); // Invoking the main function check main(); test:assertEquals(outputs.length(), 29); test:assertEquals(outputs[0], "Hex encoded hash with MD5: 0605402ee16d8e96511a58ff105bc24a"); test:assertEquals(outputs[1], "Base64 encoded hash with SHA1: /8fwbGIevBvv2Nl3gEL9DtWas+Q="); test:assertEquals(outputs[2], "Hex encoded hash with SHA256: a984a643c350b17f0738bac0fef17f2cd91d91e04596351d0af" + "670c79adc12d5"); test:assertEquals(outputs[3], "Base64 encoded hash with SHA384: lselzItgAZHQmqNbkf/s2aRjBSd93O3ayc0PB0Dxk6AEo1s4" + "4zyTz/Qp0FJO1n6b"); test:assertEquals(outputs[4], "Hex encoded hash with SHA512: a6f0770f1582f49396a97fbd5973ac22a3a578ac6a991786427" + "dfec17dbd984d8d6289771ac6f44176160a3dcd59f4a8c6b3ab97bef0caa5c67a3fac78c8e946"); test:assertEquals(outputs[5], "CRC32B for text: db9230c5"); test:assertEquals(outputs[6], "Hex encoded HMAC with MD5: b69fa2cc698e0923a7eea9d8f2b156fe"); test:assertEquals(outputs[7], "Base64 encoded HMAC with SHA1: AkWFajkb/gml703Zf4pPgxrjam4="); test:assertEquals(outputs[8], "Hex encoded HMAC with SHA256: 13a3369b8ba326fd311d4500b06a5214a02ed2a033917108f6b" + "9af58b7ede381"); test:assertEquals(outputs[9], "Base64 encoded HMAC with SHA384: 0AjKoWLhNPgdobGTPJs0PdkA0W9wkJtzUvXigzC1ZmXDJJsx" + "p4icks4JrPiwHGm6"); test:assertEquals(outputs[10], "Hex encoded HMAC with SHA512: 27588ad08e772a6bba5fca5f45cf467819c8de69a70a42be6fe" + "3eb09ceb3bfeb8b2976bda8ea5c10dcfa2294b12cb0b50b22a06309bada98af21857904a03205"); test:assertEquals(outputs[11], "Hex encoded RSA-MD5 signature: 2cfd121e4ff2409d1b2482ebbf37d0c035884d6d858e307e4" + "460b092d79cb20abb624a0dfae76b73b1fc85447be3060a36b318813f0115b1919e5efa7a7f9b117" + "3ec869f56fd9448d99770e1565db1c69a04fd0075fa0e33423a7e829a8b9c25a4dd2c68f3eee021c" + "0c4ff27979750b395384e280afd87af5274c8d2d99ad4438d9bfc9b2c5a2814212ba29ce6ff70cbe" + "30a5c23f86b0330e143c4d8813ff10092cd313c6861706d37df5f4bb4e9fc72354975ee1786cf24c" + "79b9edfa909968f198c4da37464f3d214a68fb39956717e92d667bb5a9a7f5986ba055d431813d40" + "53a028873499f98c94fd6b5c6fd5aefad432669f957ab4ce9e91c5e77b36ec0"); test:assertEquals(outputs[12], "RSA-MD5 signature verified: true"); test:assertEquals(outputs[13], "Base64 encoded RSA-SHA1 signature: bYMHKKVkjbOUp9ly3AdW9/euxF94krkkF9SDk2FfbVEc0" + "mqpGIxVoZlPiFxszurZF1YPcqOSeOehDkaHXUMfQkTjBq7XlcePtkywy0fChqw8/SWqZR8nbYv97tt8+" + "MVTkymbm26syQLwYNLiGp/EsN6X+fJpkdakwHE+TrdE+rEFrNysGyAm1DWwc4c+l7MEmSYMUnh/GWPY5" + "r2knOOdDA3mr+XyrsxzFRWZgO7ebVvEQfq9XkRp8kdiGVgpLS5au0jKj3EpbCdS1prFgy3grkuSJTTUQ" + "CwgPo7WSjWbuehFGni7rbas8HIqNlyWF0qUyznJ3eqbUwZ95QqOoVWZoQ=="); test:assertEquals(outputs[14], "RSA-SHA1 signature verified: true"); test:assertEquals(outputs[15], "Hex encoded RSA-SHA256 signature: 215c6ea96c9e82348430c6bb02e715560b4fbd3afcf24f" + "beb41ff12d4d68a797d61c4d6f822807688e4dc604e212b3cc7ac563b3cbe4e5690e2aebaf4e3df3" + "5c19d4b0f7043f50501f390634303577053b029d495104c0e98bc887f0be744ef6f726f719201907" + "ad4e86cef82eb030b60c384f7034a85159081e598e197bb8904a9123f39d190796dc7fd946157547" + "c10523999b8fa956d4119dbfe3c1435911c0585cf3c537964516706772e87f247055740cc4867ac6" + "b99d7bf699fce1b59956c7f55368c8c88c9d47e51ef120ed3f27c3e555691a697142c78cbd72c23b" + "81b43fa5ab67164a35f8e8c6bf1da187d3feb866add13f1fb9576a2f7887535311"); test:assertEquals(outputs[16], "RSA-SHA256 signature verified: true"); test:assertEquals(outputs[17], "Base64 encoded RSA-SHA384 signature: BjQ40dffGiRQ4zo1s+ld+zKhJL21RbO5sW3L2+4xmon" + "Ut126u9D4/FZ2sM1QGGamj8btB9otiYmWr9sFm4fTs1EX6vrxcCGCAiDdkMxiRs7kShaz2x/BjJQ7cOd" + "9OY+amwo7DQ/FAk9mNOt4lFUpjc9WyEW9F1PEJRXZQvMmVabDu8lp/Fh02lmEquG15DT5qT0jRxRJiS8" + "CNa+97cMZdOmF2KeADfRbNJSz70mZ76MrsNxYIXYIiJzJBQod0efQr0Sr/HDn4JDVph9rpDM3p8m94Ty" + "XvSOwxwxzZWRLEwB0ANdfDmbrW4bOpxfZZFmy1hltqNJQ9G0BcKOHsZDj6Q=="); test:assertEquals(outputs[18], "RSA-SHA384 signature verified: true"); test:assertEquals(outputs[19], "Hex encoded RSA-SHA512 signature: 15428fdc7b26d18b1f3eae4569399ae6ebfd430c8f073b" + "f2fa77ebfe1ad5645640374ea4a4aeadd252af3a198e55e69ad2a910e28470d9b54748887de06a5c" + "3ed7ab12399a404359332553e051e8ae0f3ef741faa15a21ad17a9c235e5f91d567bcca0e5a61176" + "89dccada4a33ee897514f7a8a32f12dac0087f5dcbb094c93c792f672e1685618ac5d93aa9d30f6d" + "8e306145ef2d1b9cfdc04d6c61b43376089a78471e8e03d97ee3b57e1b734a23f44366a99234a0ab" + "eb1d36d01c474833b4c2beaf430dae06ab95a1c951645fb1e0a5e7b9eed44d40e35036f2cd2764df" + "6cc04fe1248e1bb772a53c8201a974109333a318ce57930494d4cb5e41d0dc8f1c"); test:assertEquals(outputs[20], "RSA-SHA512 signature verified: true"); test:assertEquals(outputs[21], "RSA ECB PKCS1 decrypted value: Hello Ballerina!"); test:assertEquals(outputs[22], "RSA ECB OAEPwithSHA512andMGF1 decrypted value: Hello Ballerina!"); test:assertEquals(outputs[23], "AES CBC PKCS5 decrypted value: Hello Ballerina!"); test:assertEquals(outputs[24], "AES CBC no padding decrypted value: Hello Ballerina!"); test:assertEquals(outputs[25], "AES GCM PKCS5 decrypted value: Hello Ballerina!"); test:assertEquals(outputs[26], "AES GCM no padding decrypted value: Hello Ballerina!"); test:assertEquals(outputs[27], "AES ECB PKCS5 decrypted value: Hello Ballerina!"); test:assertEquals(outputs[28], "AES ECB no padding decrypted value: Hello Ballerina!"); } ================================================ FILE: examples/security-jwt-issue-validate/security_jwt_issue_validate.bal ================================================ import ballerina/io; import ballerina/jwt; public function main() returns error? { // Defines the JWT issuer configurations with the private key file configurations, // which are used to self-sign the JWT. jwt:IssuerConfig issuerConfig = { username: "ballerina", issuer: "wso2", audience: "vEwzbcasJVQm1jVYHUHCjhxZ4tYa", keyId: "NTAxZmMxNDMyZDg3MTU1ZGM0MzEzODJhZWI4NDNlZDU1OGFkNjFiMQ", expTime: 3600, // Signature can be created using either the private key configurations or keystore configurations. // For details, see https://lib.ballerina.io/ballerina/jwt/latest#IssuerSignatureConfig. signatureConfig: { config: { keyFile: "../resource/path/to/private.key" } } }; // Issues a JWT based on the provided header, payload, and private key. string jwt = check jwt:issue(issuerConfig); io:println("Issued JWT: ", jwt); // Defines the JWT validator configurations with the public certificate file configurations, // which are used to validate the signature of JWT. jwt:ValidatorConfig validatorConfig = { issuer: "wso2", audience: "vEwzbcasJVQm1jVYHUHCjhxZ4tYa", clockSkew: 60, // Signature can be validated using the public certificate file, truststore configurations, or JWKS configurations. // For detials, see https://lib.ballerina.io/ballerina/jwt/latest#ValidatorSignatureConfig. signatureConfig: { certFile: "../resource/path/to/public.crt" } }; // Validates the created JWT. jwt:Payload payload = check jwt:validate(jwt, validatorConfig); io:println("Validated JWT Payload: ", payload.toString()); } ================================================ FILE: examples/security-jwt-issue-validate/security_jwt_issue_validate.md ================================================ # JWT issue/validate This example demonstrates how to issue a self-signed JWT and validate a JWT. For more information on the underlying module, see the [`jwt` module](https://lib.ballerina.io/ballerina/jwt/latest/). >**Tip:** You may need to change the certificate file path, private key file path, and trusted certificate file path in the code below. ::: code security_jwt_issue_validate.bal ::: Run the program by executing command below. ::: out security_jwt_issue_validate.out ::: ================================================ FILE: examples/security-jwt-issue-validate/security_jwt_issue_validate.metatags ================================================ description: BBE on how to issue and validate a JSON Web Token (JWT) in Ballerina. keywords: ballerina, ballerina by example, jwt, jwt issuer, jwt validator ================================================ FILE: examples/security-jwt-issue-validate/security_jwt_issue_validate.out ================================================ $ bal run security_jwt_issue_validate.bal Issued JWT: eyJhbGciOiJSUzI1NiIsICJ0eXAiOiJKV1QiLCAia2lkIjoiTlRBeFptTXhORE15WkR nM01UVTFaR00wTXpFek9ESmhaV0k0TkRObFpEVTFPR0ZrTmpGaU1RIn0.eyJpc3MiOi JiYWxsZXJpbmEiLCAic3ViIjoiYWRtaW4iLCAiYXVkIjoidkV3emJjYXNKVlFtMWpWW UhVSENqaHhaNHRZYSIsICJqdGkiOiI1NWEwYjc1NC04OTVmLTQyNzktODg0My1iNzQ1 ZTExYTU3ZTkiLCAiZXhwIjoxNjExMTI3MDIzLCAibmJmIjoxNjExMTIzNDIzLCAiaWF 0IjoxNjExMTIzNDIzfQ.DMJDjJEFiQN7d_2CXGfXX_UR8Fi7Witr3aVGm4K7amEm3xN cbh1bZmKO2ir-oP2_ikoM1_ETO7i4E4LKJHNAEdhqj8YHyKpbszaEq5zouMOtdFcI7i TS8LyYDnyLEQQ6sa9L9NoMz3xULeF8epk0eaN1vVA-ijndVkZlMjaXJNf9Bgzn2qJOd sQ6F0GeC4WKEt-xcEY5C2_haEDotSOYhUzEqh6D1fRtrGy7GaH5gzx99n-xjn8NZbTD F0VnD6c1kJPe25FiPz24l9KdaCE1i2WbuzEhZWMclHW5RcTXVkLLkjQ4DvxfE-riGmK qPN1gatWViZQF_VGBK-G7rEhi9Q Validated JWT Payload: {"iss":"ballerina","sub":"admin", "aud":"vEwzbcasJVQm1jVYHUHCjhxZ4tYa", "jti":"55a0b754-895f-4279-8843-b745e11a57e9", "exp":1611127023,"nbf":1611123423,"iat":1611123423} ================================================ FILE: examples/security-jwt-issue-validate/tests/security_jwt_issue_validate_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... val) { outputs.push(val.reduce(function (any a, any b) returns string => a.toString() + b.toString(), "").toString()); } @test:Config {} function testFunc() returns error? { test:when(mock_printLn).call("mockPrint"); // Invokes the main function. check main(); test:assertEquals(outputs.length(), 2); test:assertTrue(outputs[0].includes("Issued JWT: eyJhbGciOiJSUzI1NiIsICJ0eXAiOiJKV1QiLCAia2" + "lkIjoiTlRBeFptTXhORE15WkRnM01UVTFaR00wTXpFek9ESmhaV0k0TkRObFpEVTFPR0ZrTmpGaU1RIn0.")); test:assertTrue(outputs[1].includes("Validated JWT Payload: {\"iss\":\"wso2\",\"sub\":" + "\"ballerina\",\"aud\":\"vEwzbcasJVQm1jVYHUHCjhxZ4tYa\",")); } ================================================ FILE: examples/send-email/send_email.bal ================================================ import ballerina/email; public function main() returns error? { // Creates an SMTP client with the connection parameters, host, username, and password. // The default port number `465` is used over SSL with these configurations. `SmtpConfig` can // be configured and passed to this client if the port or security is to be customized. email:SmtpClient smtpClient = check new ("smtp.email.com", "sender@email.com" , "pass123"); // Defines the email that is required to be sent. email:Message email = { // "TO", "CC", and "BCC" addresses can be added as follows. // Only the "TO" address is mandatory out of these three. to: "receiver1@email.com", cc: "receiver2@email.com", bcc: "receiver3@email.com", // Subject of the email is added as follows. This field is mandatory. subject: "Sample Email", // Body content (text) of the email is added as follows. This field is optional. body: "This is a sample email." }; // Sends the email message with the client. The `send` method can be used instead if the // email is required to be sent with mandatory and optional parameters instead of // configuring an `email:Message` record. check smtpClient->sendMessage(email); } ================================================ FILE: examples/send-email/send_email.md ================================================ # Email client - Send email The `email:SmtpClient` connects to a given SMTP server to send emails. An `email:SmtpClient` is created by providing the hostname and required credentials. Once connected, the `sendMessage` or the `send` methods are used to send emails. An `email:Message` record, which contains the required information about the email can be passed to the `sendMessage` method. If additional information is not required, the `send` method can be used only with mandatory parameters such as `to`, `from`, `subject`, and `body`. ::: code send_email.bal ::: ## Prerequisites - SMTP server should be up and running. Run the SMTP client by executing the following command. ::: out send_email.out ::: ## Related links - [`email:SmtpClient` client object - API documentation](https://lib.ballerina.io/ballerina/email/latest#SmtpClient) - [`email:Message` record - API documentation](https://lib.ballerina.io/ballerina/email/latest#Message) - [SMTP client - Specification](https://ballerina.io/spec/email/#31-smtp-client) ================================================ FILE: examples/send-email/send_email.metatags ================================================ description: This BBE is about sending emails. keywords: ballerina, ballerina by example, bbe, email, SMTP ================================================ FILE: examples/send-email/send_email.out ================================================ $ bal run send_email.bal # Check the inbox to view the sent emails. ================================================ FILE: examples/sensitive-data-logging/sensitive_data_logging.bal ================================================ import ballerina/log; // Custom masking function isolated function maskString(string input) returns string { if input.length() <= 2 { return "****"; } return input.substring(0, 1) + "****" + input.substring(input.length() - 1); } // User record with sensitive data annotations type User record { string id; @log:Sensitive string password; @log:Sensitive { strategy: { replacement: "****" } } string creditCard; @log:Sensitive { strategy: { replacement: maskString } } string ssn; string name; }; public function main() returns error? { // Create user with sensitive information User user = { id: "U001", password: "mypassword", creditCard: "4111-1111-1111-1234", ssn: "123-45-6789", name: "John Doe" }; // Log user details - sensitive fields will be masked automatically log:printInfo("User details", user = user); } ================================================ FILE: examples/sensitive-data-logging/sensitive_data_logging.md ================================================ # Sensitive data logging This example demonstrates how to mask sensitive data in log messages using the `@log:Sensitive` annotation. The Ballerina log module provides built-in capabilities to protect personally identifiable information (PII) and other sensitive data from being exposed in logs. By default, sensitive data masking is disabled and **must be enabled** in the configuration file. ::: code Config.toml ::: There are three masking strategies available: 1. **Exclude strategy (default)**: The sensitive field is completely excluded from log output. 2. **Fixed replacement**: The sensitive field is replaced with a fixed string (e.g., "****"). 3. **Custom function replacement**: A custom function is used to mask the sensitive field. ::: code sensitive_data_logging.bal ::: Run the example: ::: out sensitive_data_logging.out ::: ## Related links - [`log` module - Specification](https://ballerina.io/spec/log/#5-sensitive-data-masking) - [`log` module - API documentation](https://lib.ballerina.io/ballerina/log/latest) ================================================ FILE: examples/sensitive-data-logging/sensitive_data_logging.metatags ================================================ description: BBE on how to mask sensitive data in logs using @log:Sensitive annotation with different masking strategies. keywords: ballerina, ballerina by examples, bbe, log, sensitive data, masking, exclude, replace, PII, security ================================================ FILE: examples/sensitive-data-logging/sensitive_data_logging.out ================================================ $ bal run sensitive_data_logging.bal time=2025-10-07T15:32:20.226+05:30 level=INFO module="" message="User details" user={"id":"U001","creditCard":"****","ssn":"1****9","name":"John Doe"} ================================================ FILE: examples/sequence-diagrams/sequence_diagrams.bal ================================================ import ballerina/http; import ballerina/io; // A function can be viewed as a sequence diagram. // The diagram has a lifeline (vertical line) for each worker (both named worker and function's default worker). public function main() returns error? { // The diagram also has a lifeline for each client object parameter or variable in // the initialization section, representing the remote system to which the client object is sending messages. http:Client cl = check new ("https://run.mocky.io"); // Each remote method call on a client object is represented as a horizontal line // between the lifeline of the worker making the call and the remote system. string payload = check cl->get("/v3/55cf043f-f5d4-4005-af3c-cce4a955afc7/"); io:println(payload); return; } ================================================ FILE: examples/sequence-diagrams/sequence_diagrams.md ================================================ # Sequence diagrams A function can be viewed as a sequence diagram as shown below. ![Sequence diagrams](/learn/by-example/images/sequence-diagram.png "Sequence Diagram") The diagram has a lifeline (vertical line) for each worker (both named worker and function's default worker). The diagram also has a lifeline for each client object parameter or variable in the initialization section, representing the remote system to which the client object is sending messages. Each remote method call on a client object is represented as a horizontal line between the lifeline of the worker making the call and the remote system. ::: code sequence_diagrams.bal ::: ::: out sequence_diagrams.out ::: ================================================ FILE: examples/sequence-diagrams/sequence_diagrams.metatags ================================================ description: This BBE demonstrates Ballerina's sequence diagrams keywords: ballerina, ballerina by example, bbe, sequence diagram, diagram ================================================ FILE: examples/sequence-diagrams/sequence_diagrams.out ================================================ $ bal run sequence_diagrams.bal Hello World ================================================ FILE: examples/service-declaration/service_declaration.bal ================================================ import ballerina/io; import ballerina/udp; // You can combine a listener declaration with a service declaration as shown in this example. service on new udp:Listener(8080) { remote function onDatagram(readonly & udp:Datagram dg) { io:println("bytes received: ", dg.data.length()); } } ================================================ FILE: examples/service-declaration/service_declaration.client.out ================================================ $ nc -u localhost 8080 Hello, Ballerina ================================================ FILE: examples/service-declaration/service_declaration.md ================================================ # Service declaration A service represents a collection of remotely accessible methods attached to a particular listener. A service declaration is syntactic sugar for creating services in Ballerina. A service declaration gets desugared into several things including creating a listener object, registering it with the module, creating a service object, attaching the service object to the listener object, etc,. The type of the listener determines required type of remote methods. ::: code service_declaration.bal ::: Run the service using the `bal run` command. ::: out service_declaration.server.out ::: Use the following Netcat (or nc) command to send packets to the service. ::: out service_declaration.client.out ::: ================================================ FILE: examples/service-declaration/service_declaration.metatags ================================================ description: This BBE demonstrates how to write a service in Ballerina. keywords: ballerina, ballerina by example, bbe, service, UDP, service declaration, providing services ================================================ FILE: examples/service-declaration/service_declaration.server.out ================================================ $ bal run service_declaration.bal bytes received: 17 ================================================ FILE: examples/sftp-client-receive-file/sftp_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 // `22` for SSH is used with these configurations. ftp:Client fileClient = check new ({ protocol: ftp:SFTP, host: "sftp.example.com", port: 22, auth: { credentials: { username: "user1", password: "pass456" }, // Private key file location and its password (if encrypted) is // given corresponding to the SSH key file used in the SFTP client. privateKey: { path: "../resource/path/to/private.key", password: "keyPass123" } } }); // Reads a file from a 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/sftp-client-receive-file/sftp_client_receive_file.md ================================================ # SFTP client - Receive file The `ftp:Client` connects to a given SFTP server, and then sends and receives files as byte streams. An `ftp:Client` with SFTP protocol is created by giving the protocol, host-name and required credentials and the private key. Once connected, `get` method is used to read files as byte streams from the SFTP server. Use this to transfer files from a remote file system to a local file system. ::: code sftp_client_receive_file.bal ::: ## Prerequisites - Start a [SFTP server](https://hub.docker.com/r/atmoz/sftp/) instance. - Run the SFTP client given in the [SFTP client - Send file](/learn/by-example/sftp-client-send-file) example to put a file in the SFTP server. Run the program by executing the following command. The newly-added file will appear in the local directory. ::: out sftp_client_receive_file.out ::: ## Related links - [`ftp:Client->get` method - API documentation](https://lib.ballerina.io/ballerina/ftp/latest#Client#get) - [SFTP client - Specification](/spec/ftp/#322-secure-client) ================================================ FILE: examples/sftp-client-receive-file/sftp_client_receive_file.metatags ================================================ description: This example demonstrates getting file content from a remote SFTP server. keywords: ballerina, ballerina by example, bbe, SFTP, FTP security ================================================ FILE: examples/sftp-client-receive-file/sftp_client_receive_file.out ================================================ $ bal run sftp_client.bal ================================================ FILE: examples/sftp-client-send-file/sftp_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 // `22` for SSH is used with these configurations. ftp:Client fileClient = check new ({ protocol: ftp:SFTP, host: "sftp.example.com", port: 22, auth: { credentials: { username: "user1", password: "pass456" }, // Private key file location and its password (if encrypted) is // given corresponding to the SSH key file used in the SFTP client. privateKey: { path: "../resource/path/to/private.key", password: "keyPass123" } } }); // 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 fileStream = check io:fileReadBlocksAsStream("./local/logFile.txt", 1024); check fileClient->put("/server/logFile.txt", fileStream); check fileStream.close(); } ================================================ FILE: examples/sftp-client-send-file/sftp_client_send_file.md ================================================ # SFTP client - Send file The `ftp:Client` connects to a given SFTP server, and then sends and receives files as byte streams. A `ftp:Client` with SFTP protocol is created by giving the protocol, host-name, required credentials and the private key. Once connected, `put` method is used to write files as byte streams to the SFTP server. Use this to transfer files from a local file system to a remote file system. ::: code sftp_client_send_file.bal ::: ## Prerequisites - Start a [SFTP server](https://hub.docker.com/r/atmoz/sftp/) instance. Run the program by executing the following command. The newly-added file will appear in the SFTP server. ::: out sftp_client_send_file.out ::: ## Related links - [`ftp:Client->put` method - API documentation](https://lib.ballerina.io/ballerina/ftp/latest#Client#put) - [SFTP client - Specification](/spec/ftp/#322-secure-client) ================================================ FILE: examples/sftp-client-send-file/sftp_client_send_file.metatags ================================================ description: This example demonstrates putting file content on a remote SFTP server. keywords: ballerina, ballerina by example, bbe, SFTP, FTP security ================================================ FILE: examples/sftp-client-send-file/sftp_client_send_file.out ================================================ $ bal run sftp_client.bal ================================================ FILE: examples/sftp-service-receive-file/sftp_service_receive_file.bal ================================================ import ballerina/ftp; import ballerina/io; // Creates the listener with the connection parameters and the protocol-related // configuration. The listener listens to the files // with the given file name pattern located in the specified path. listener ftp:Listener fileListener = new ({ protocol: ftp:SFTP, host: "sftp.example.com", auth: { credentials: { username: "user1", password: "pass456" }, privateKey: { path: "../resource/path/to/private.key", password: "keyPass123" } }, port: 22, path: "/home/in", fileNamePattern: "(.*).txt" }); // One or many services can listen to the SFTP listener for the // periodically-polled file related events. service on fileListener { // When a file event is successfully received, the `onFileChange` method is called. remote function onFileChange(ftp:WatchEvent & readonly event, ftp:Caller caller) returns error? { // `addedFiles` contains the paths of the newly-added files/directories // after the last polling was called. foreach ftp:FileInfo addedFile in event.addedFiles { // Get the newly added file from the SFTP server as a `byte[]` stream. stream fileStream = check caller->get(addedFile.pathDecoded); // Write the content to a file. check io:fileWriteBlocksFromStream(string `./local/${addedFile.name}`, fileStream); check fileStream.close(); } } } ================================================ FILE: examples/sftp-service-receive-file/sftp_service_receive_file.md ================================================ # SFTP service - Receive file The `ftp:Service` connects to a given SFTP server via the `ftp:Listener`. A `ftp:Listener` with SFTP protocol is created by providing the protocol, host-name, required credentials, and the private key. Once connected, service starts receiving events every time a file is deleted or added to the server. To take action for these events `ftp:Caller` is used. The `ftp:Caller` can be specified as a parameter of `onFileChange` remote method. The `ftp:Caller` allows interacting with the server via `get`, `append`, `delete`, etc remote methods. Use this to listen to file changes occurring in a remote file system and take action for those changes. ::: code sftp_service_receive_file.bal ::: ## Prerequisites - Start a [SFTP server](https://hub.docker.com/r/atmoz/sftp/) instance. Run the program by executing the following command. Each newly added file in the SFTP server will be saved in the local file system. ::: out sftp_service_receive_file.out ::: >**Tip:** Run the SFTP client given in the [SFTP client - Send file](/learn/by-example/sftp-client-send-file) example to put a file in the SFTP server. ## Related links - [`ftp:Listener` client object - API documentation](https://lib.ballerina.io/ballerina/ftp/latest#Listener) - [SFTP service - Specification](/spec/ftp/#422-secure-listener) ================================================ FILE: examples/sftp-service-receive-file/sftp_service_receive_file.metatags ================================================ description: This example demonstrates receiving file/directory changes that occur in a remote SFTP server and using an ftp:Caller to read the contents of the newly added files. keywords: ballerina, ballerina by example, bbe, SFTP, remote file, listener, FTP security ================================================ FILE: examples/sftp-service-receive-file/sftp_service_receive_file.out ================================================ $ bal run sftp_service_read.bal ================================================ FILE: examples/sftp-service-send-file/sftp_service_send_file.bal ================================================ import ballerina/ftp; import ballerina/io; // Creates the listener with the connection parameters and the protocol-related // configuration. The listener listens to the files // with the given file name pattern located in the specified path. listener ftp:Listener fileListener = new ({ protocol: ftp:SFTP, host: "sftp.example.com", auth: { credentials: { username: "user1", password: "pass456" }, privateKey: { path: "../resource/path/to/private.key", password: "keyPass123" } }, port: 22, path: "/home/in", fileNamePattern: "(.*).txt" }); // One or many services can listen to the SFTP listener for the // periodically-polled file related events. service on fileListener { // When a file event is successfully received, the `onFileChange` method is called. remote function onFileChange(ftp:WatchEvent event, ftp:Caller caller) returns error? { // `addedFiles` contains the paths of the newly-added files/directories // after the last polling was called. foreach ftp:FileInfo addedFile in event.addedFiles { // The `ftp:Caller` can be used to append another file to the added files in the server. stream fileStream = check io:fileReadBlocksAsStream("./local/appendFile.txt", 7); check caller->append(addedFile.pathDecoded, fileStream); check fileStream.close(); } } } ================================================ FILE: examples/sftp-service-send-file/sftp_service_send_file.md ================================================ # SFTP service - Send file The `ftp:Service` connects to a given SFTP server via the `ftp:Listener`. Once connected, service starts receiving events every time a file is deleted or added to the server. To take action for these events `ftp:Caller` is used. The `ftp:Caller` can be specified as a parameter of `onFileChange` remote method. The `ftp:Caller` allows interacting with the server via `get`, `append`, `delete`, etc remote methods. Use this to listen to file changes occurring in a remote file system and take action for those changes. ::: code sftp_service_send_file.bal ::: ## Prerequisites - Start a [SFTP server](https://hub.docker.com/r/atmoz/sftp/) instance. Run the program by executing the following command. Each newly added file in the SFTP server will be appended with the content in the appending file. ::: out sftp_service_send_file.out ::: >**Tip:** Run the SFTP client given in the [SFTP client - Send file](/learn/by-example/sftp-client-send-file) example to put a file in the SFTP server. ## Related links - [`ftp:Caller` client object - API documentation](https://lib.ballerina.io/ballerina/ftp/latest#Caller) - [`ftp:Caller` functions - Specification](/spec/ftp/#52-functions) ================================================ FILE: examples/sftp-service-send-file/sftp_service_send_file.metatags ================================================ description: This example demonstrates receiving file/directory changes that occur in a remote SFTP server and using an ftp:Caller to append a file to the newly added files. keywords: ballerina, ballerina by example, bbe, SFTP, remote file, listener, FTP security ================================================ FILE: examples/sftp-service-send-file/sftp_service_send_file.out ================================================ $ bal run sftp_service_read_write.bal ================================================ FILE: examples/single-use-of-typed-binding/single_use_of_typed_binding.bal ================================================ import ballerina/io; type Person record { int id; string fname; string lname; }; public function main() { // A typed list binding followed by `=` in which the values of the list members are bound to // the `name` and `age` variables in the binding. [string, int] [name, age] = getPersonInfo(); io:println(name, " ", age); // The same can be written using `var`. var [personName, personAge] = getPersonInfo(); io:println(personName, " ", personAge); // Other binding patterns can also be used as such. // Here, a typed mapping binding is followed by `=` in which the values of the record fields // are bound to the `id`, `fname`, and `lname` variables of the binding. Person {id, fname, lname} = getPerson(); io:println(id, " ", fname, " ", lname); } function getPersonInfo() returns [string, int] { return ["John", 30]; } function getPerson() returns Person { return {id: 1001, fname: "Anne", lname: "Frank"}; } ================================================ FILE: examples/single-use-of-typed-binding/single_use_of_typed_binding.md ================================================ # Single use of typed binding patterns The single use of typed binding patterns is usually followed by an `=` operator. ::: code single_use_of_typed_binding.bal ::: ::: out single_use_of_typed_binding.out ::: ## Related links - [Binding patterns](/learn/by-example/binding-patterns/) - [Typed binding pattern](/learn/by-example/typed-binding-pattern/) - [Single use with on fail clause](/learn/by-example/single-use-with-on-fail-clause/) - [Iterative use of typed binding patterns](/learn/by-example/iterative-use-of-typed-binding/) ================================================ FILE: examples/single-use-of-typed-binding/single_use_of_typed_binding.metatags ================================================ description: This BBE demonstrates the single use of typed binding patterns, single use of list binding, single use of mapping binding, single use of error binding, typed binding followed by `=`, list binding followed by `=`, mapping binding followed by `=`, and error binding followed by `=`. keywords: ballerina, ballerina by example, bbe, binding pattern, typed binding, single use, list binding, mapping binding, error binding ================================================ FILE: examples/single-use-of-typed-binding/single_use_of_typed_binding.out ================================================ $ bal run single_use_of_typed_binding.bal John 30 John 30 1001 Anne Frank ================================================ FILE: examples/single-use-with-on-fail-clause/single_use_with_on_fail_clause.bal ================================================ import ballerina/io; public function main() { do { _ = check int:fromString("Hello"); // A typed capture binding pattern is used with an `on fail` clause. // It matches the associated error value in the clause and binds the value // to the `err` variable. } on fail error err { io:println(err.message()); } } ================================================ FILE: examples/single-use-with-on-fail-clause/single_use_with_on_fail_clause.md ================================================ # Single use of typed binding patterns with `on fail` clause The only exception where the single use is not followed by an `=` operator is when it is used with an `on fail` clause. ::: code single_use_with_on_fail_clause.bal ::: ::: out single_use_with_on_fail_clause.out ::: ## Related links - [Binding patterns](/learn/by-example/binding-patterns/) - [Typed binding pattern](/learn/by-example/typed-binding-pattern/) - [Single use of typed binding patterns](/learn/by-example/single-use-of-typed-binding/) - [Iterative use of typed binding patterns](/learn/by-example/iterative-use-of-typed-binding/) ================================================ FILE: examples/single-use-with-on-fail-clause/single_use_with_on_fail_clause.metatags ================================================ description: This BBE demonstrates the single use of typed binding patterns, single use of typed binding with the `on fail` clause, and use of capture binding with the `on fail` clause. keywords: ballerina, ballerina by example, bbe, binding pattern, typed binding, single use, on fail ================================================ FILE: examples/single-use-with-on-fail-clause/single_use_with_on_fail_clause.out ================================================ $ bal run single_use_with_on_fail_clause.bal {ballerina/lang.int}NumberParsingError ================================================ FILE: examples/singleton-types/singleton_types.bal ================================================ import ballerina/io; // The `SwitchStatus` type is a union type of two singleton types // defined using string literals, which are simple constant expressions. type SwitchStatus "on"|"off"; // The `shouldToggleSwitch` function has two parameters of the `SwitchStatus` type, // restricting the arguments to be either "on" or "off". Trying to pass as an argument // a value that cannot be guaranteed to be either "on" or "off" will result // in a compile-time error. function shouldToggleSwitch(SwitchStatus currentStatus, SwitchStatus newStatus) returns boolean { return currentStatus != newStatus; } // The type of the `STATUS_OFF` constant is the singleton type containing only // the "off" value. const STATUS_OFF = "off"; // The type of the `DEFAULT_CONFIG` constant is the singleton type containing only // an immutable mapping value with exactly two fields: a `name` field with the value // "default" and a `status` field with the value "off". const DEFAULT_CONFIG = { name: "default", status: STATUS_OFF }; public function main() { // The `shouldToggleSwitch` function can be called only with "on" or "off" as arguments. boolean b1 = shouldToggleSwitch("on", "off"); io:println(b1); // The type of `arg1` is the singleton type containing only the "off" value. "off" arg1 = "off"; // A constant can be used as a singleton type. // The type of `arg2` is also the singleton type containing only the "off" value. // On the left-hand side, `STATUS_OFF` is used as a type. // On the right-hand side, `STATUS_OFF` is used as a value. STATUS_OFF arg2 = STATUS_OFF; boolean b2 = shouldToggleSwitch(arg1, arg2); io:println(b2); // An immutable mapping value that has exactly the same fields as the // `DEFAULT_CONFIG` constant. map & readonly mv1 = { name: "default", status: "off" }; // An immutable mapping value that is not the same as the `DEFAULT_CONFIG` // value since it has "on" as the value for the `status` field. map & readonly mv2 = { name: "default", status: "on" }; // The `DEFAULT_CONFIG` constant is used as a type in the `is` check. // Since it is a singleton type, the `is` check will evaluate to true // only for an immutable value that has exactly the same fields. io:println(mv1 is DEFAULT_CONFIG); io:println(mv2 is DEFAULT_CONFIG); } ================================================ FILE: examples/singleton-types/singleton_types.md ================================================ # Singleton types A singleton type is a type that contains exactly one value. A singleton type is described using a compile-time constant expression. When the simple constant expression is a variable reference to a structured value, the value will be an immutable value. Therefore, a mutable value will not belong to any singleton type. Unions of singletons enable the definition of more refined types, constraining the value space to include exactly the allowed values, and thereby, removing the need for explicit validation. The type of a constant is a singleton type and a constant can be used as a singleton type. Enumerations are shorthand for unions of string constants, and therefore, the members of an enumeration can also be used as singleton types. ::: code singleton_types.bal ::: ::: out singleton_types.out ::: ## Related links - [Constants](/learn/by-example/const-and-final/) - [Enumerations](/learn/by-example/enumerations/) - [Immutability](/learn/by-example/immutability/) ================================================ FILE: examples/singleton-types/singleton_types.metatags ================================================ description: This BBE demonstrates singleton types in Ballerina. keywords: ballerina, ballerina by example, bbe, singletons, singleton types ================================================ FILE: examples/singleton-types/singleton_types.out ================================================ $ bal run singleton_types.bal true false true false ================================================ FILE: examples/soap-client-security-inbound-security-config/soap_client_security_inbound_security_config.bal ================================================ import ballerina/soap.soap12; import ballerina/crypto; public function main() returns error? { crypto:KeyStore keyStore = { path: "/path/to/keyStore.p12", password: "keyStorePassword" }; crypto:KeyStore decryptionKeyStore = { path: "/path/to/keyStore.p12", password: "keyStorePassword" }; soap12:Client soapClient = check new ("http://soap-endpoint.com?wsdl", { inboundSecurity: { signatureKeystore: keyStore, decryptKeystore: decryptionKeyStore } } ); xml body = xml ` `; check soapClient->sendOnly(body); } ================================================ FILE: examples/soap-client-security-inbound-security-config/soap_client_security_inbound_security_config.md ================================================ # SOAP client security - Inbound security configuration The `soap` client can be configured to apply inbound security settings when a request is made. These configurations include various web service security policies such as `Username Token`, `Timestamp Token`, `X509 Token`, `Symmetric Binding`, `Asymmetric Binding`, and `Transport Binding`. These configurations can be implemented individually or in combination with one another. After applying the security configuration, the required security tags will be included under the `` tag within the SOAP envelope. ::: code soap_client_security_inbound_security_config.bal ::: Run the program by executing the following command. ::: out soap_client_security_inbound_security_config.out ::: ## Related links - [`soap` module - API documentation](https://central.ballerina.io/ballerina/soap/) - [SOAP client security inbound security configuration - Specification](/spec/soap/#321-inbound-security-configurations) ================================================ FILE: examples/soap-client-security-inbound-security-config/soap_client_security_inbound_security_config.metatags ================================================ description: This example demonstrates applying inbound security configurations for SOAP envelopes when the request is made. keywords: ballerina, ballerina by example, soap, client, security, inbound security ================================================ FILE: examples/soap-client-security-inbound-security-config/soap_client_security_inbound_security_config.out ================================================ $ bal run soap_client_security_inbound_security_config.bal ================================================ FILE: examples/soap-client-security-outbound-security-config/soap_client_security_outbound_security_config.bal ================================================ import ballerina/crypto; import ballerina/soap.soap12; public function main() returns error? { crypto:KeyStore keyStore = { path: "/path/to/keyStore.p12", password: "keyStorePassword" }; crypto:KeyStore decryptionKeyStore = { path: "/path/to/decryptionKeyStore.p12", password: "keyStorePassword" }; soap12:Client soapClient = check new ("http://soap-endpoint.com?wsdl", { outboundSecurity: { signatureConfig: { keystore: keyStore, privateKeyAlias: "private-key-alias", privateKeyPassword: "private-key-password" }, encryptionConfig: { keystore: decryptionKeyStore, publicKeyAlias: "public-key-alias" } } } ); xml body = xml ` `; check soapClient->sendOnly(body); } ================================================ FILE: examples/soap-client-security-outbound-security-config/soap_client_security_outbound_security_config.md ================================================ # SOAP client security - Outbound security configuration The `soap` client can be configured to apply outbound security configurations for decrypting the data in the SOAP response, and verifying the digital signature for security validation. The configurations can be set by passing `soap:OutboundSecurityConfig` to the `outboundSecurity` field of the client. ::: code soap_client_security_outbound_security_config.bal ::: Run the program by executing the following command. ::: out soap_client_security_outbound_security_config.out ::: ## Related links - [`soap` module - API documentation](https://central.ballerina.io/ballerina/soap/) - [SOAP client security outbound security configuration - Specification](/spec/soap/#322-outbound-security-configurations) ================================================ FILE: examples/soap-client-security-outbound-security-config/soap_client_security_outbound_security_config.metatags ================================================ description: This example demonstrates applying outbound security configurations to decrypt and verify signatures in SOAP responses. keywords: ballerina, ballerina by example, soap, client, security, outbound security ================================================ FILE: examples/soap-client-security-outbound-security-config/soap_client_security_outbound_security_config.out ================================================ $ bal run soap_client_security_outbound_security_config.bal ================================================ FILE: examples/soap-client-security-ssl-tsl/soap_client_security_ssl_tls.bal ================================================ import ballerina/soap.soap12; public function main() returns error? { soap12:Client soapClient = check new ("https://soap-endpoint.com?wsdl", { httpConfig: { secureSocket: { cert: "../resource/path/to/public.crt" } } } ); xml body = xml ` `; check soapClient->sendOnly(body); } ================================================ FILE: examples/soap-client-security-ssl-tsl/soap_client_security_ssl_tls.md ================================================ # SOAP client security - SSL/TLS The `soap` client can be configured to apply HTTP security configurations using the `httpConfig` parameter. SSL certificate can be applied using the `secureSocket` field to establish an HTTPS connection between the client and the server. The SOAP client supports all client security configurations supported by the HTTP client. ::: code soap_client_security_ssl_tls.bal ::: Run the program by executing the following command. ::: out soap_client_security_ssl_tls.out ::: ## Related links - [`soap` module - API documentation](https://central.ballerina.io/ballerina/soap/) - [HTTP client security - Specification](https://ballerina.io/spec/http/#2411-security) - [`soap` - Specification](/spec/soap) ================================================ FILE: examples/soap-client-security-ssl-tsl/soap_client_security_ssl_tls.metatags ================================================ description: This example demonstrates applying HTTP security configurations for a Ballerina SOAP client. keywords: ballerina, ballerina by example, soap, http, client, security, ssl, tls ================================================ FILE: examples/soap-client-security-ssl-tsl/soap_client_security_ssl_tls.out ================================================ $ bal run soap_client_security_ssl_tls.bal ================================================ FILE: examples/soap-client-send-receive/soap_client_send_receive.bal ================================================ import ballerina/io; import ballerina/soap.soap12; xmlns "http://tempuri.org/" as quer; public function main() returns error? { int additionA = 37; int additionB = 73; // The SOAP 1.2 client connects to a live SOAP endpoint // that executes basic arithmetic operations soap12:Client soapClient = check new ("http://www.dneonline.com/calculator.asmx?WSDL"); // The SOAP envelope is constructed including necessary numerical values // for the addition operation. xml body = xml ` ${additionA} ${additionB} `; // `sendOnly()` fires and forgets a request. check soapClient->sendOnly(body, "http://tempuri.org/Add"); // `sendReceive()` sends a request to a SOAP endpoint // and receives the response in `xml` or `mime:Entity[]` format. xml response = check soapClient->sendReceive(body, "http://tempuri.org/Add"); xml result = response/**//*; io:println(string `Sum: ${additionA} + ${additionB} = `, result); } ================================================ FILE: examples/soap-client-send-receive/soap_client_send_receive.md ================================================ # SOAP client - Send/Receive The `soap` module provides APIs to connect to a SOAP endpoint that supports SOAP 1.1 or 1.2 versions. Users can specify the version when importing the `soap` module. The `sendReceive()` API will send a request to a SOAP endpoint and bind the response to one of the `xml` or `mime:Entity[]` data types determined by the user. The `sendOnly()` API will fire and forget a SOAP request. ::: code soap_client_send_receive.bal ::: Run the program by executing the following command. ::: out soap_client_send_receive.out ::: ## Related links - [`soap` module - API documentation](https://central.ballerina.io/ballerina/soap/) - [`soap` Client - Specification](/spec/soap/#21-client) ================================================ FILE: examples/soap-client-send-receive/soap_client_send_receive.metatags ================================================ description: This example demonstrates connecting and interacting with a SOAP endpoint via a Ballerina SOAP client. keywords: ballerina, ballerina by example, soap, client ================================================ FILE: examples/soap-client-send-receive/soap_client_send_receive.out ================================================ $ bal run soap_client_send_receive.bal Sum: 37 + 73 = 110 ================================================ FILE: examples/sort-iterable-objects/sort_iterable_objects.bal ================================================ import ballerina/io; type Employee record { string firstName; string lastName; decimal salary; }; public function main() { Employee[] employees = [ {firstName: "Jones", lastName: "Welsh", salary: 1000.00}, {firstName: "Anne", lastName: "Frank", salary: 5000.00}, {firstName: "Rocky", lastName: "Irving", salary: 6000.00}, {firstName: "Anne", lastName: "Perera", salary: 3000.00}, {firstName: "Jermaine", lastName: "Perera", salary: 4000.00}, {firstName: "Rocky", lastName: "Puckett", salary: 6000.00}, {firstName: "Jermaine", lastName: "Kent", salary: 4000.00} ]; Employee[] sorted = from var e in employees // The `order by` clause sorts the output items based on the given // `order-key` and the `order-direction`. // The `order-key` must be an ordered type. // The `order-direction` is `ascending` if not specified explicitly. order by e.firstName ascending, e.lastName descending select e; foreach Employee e in sorted { io:println(e.firstName, " ", e.lastName); } } ================================================ FILE: examples/sort-iterable-objects/sort_iterable_objects.md ================================================ # Sort iterable objects The `order by` clause in the query expression can be used to sort the elements in a collection. Ordering works consistently with the `<`, `<=`, `>`, `>=` operators. Some comparisons involving `()` and float `NaN` are considered unordered. Therefore, if these unordered types are encountered in the query, they will be returned as the last elements of the ordered collection. The syntax to write an `order by` clause is `order by expression orderDirection`. The order direction can be `ascending` or `descending`. ::: code sort_iterable_objects.bal ::: ::: out sort_iterable_objects.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/sort-iterable-objects/sort_iterable_objects.metatags ================================================ description: This BBE demonstrates ordering in Ballerina, sorting iterable objects, sorting a table, and sorting a list, result, or a collection. keywords: ballerina, ballerina by example, bbe, order by clause, query, query expression, order, sort, ascending, descending, collection ================================================ FILE: examples/sort-iterable-objects/sort_iterable_objects.out ================================================ $ bal run sort_iterable_objects.bal Anne Perera Anne Frank Jermaine Perera Jermaine Kent Jones Welsh Rocky Puckett Rocky Irving ================================================ FILE: examples/start-service-from-service-class-definition/start_service_from_service_class_definition.bal ================================================ import ballerina/http; import ballerina/lang.runtime; public type Album readonly & record {| string title; string artist; |}; // The listener object is attached to the `albumService`. // It receives inputs from the specified port 9090 and invokes the remote methods. http:Listener httpListener = check new (9090); public service class Service { *http:Service; table key(title) albums = table [ {title: "Blue Train", artist: "John Coltrane"}, {title: "Jeru", artist: "Gerry Mulligan"} ]; resource function get albums() returns Album[] { return self.albums.toArray(); } resource function post albums(Album album) { self.albums.add(album); } } public function main() returns error? { // Create an HTTP service. http:Service albumService = new Service(); // Attach the service to the listener along with the resource path. check httpListener.attach(albumService); // Start the listener. check httpListener.'start(); // Register the listener dynamically. runtime:registerListener(httpListener); } ================================================ FILE: examples/start-service-from-service-class-definition/start_service_from_service_class_definition.client.out ================================================ $ curl http://localhost:9090/albums [{"title":"Blue Train", "artist":"John Coltrane"}, {"title":"Jeru", "artist":"Gerry Mulligan"}] $ curl http://localhost:9090/albums -H "Content-type:application/json" -d "{\"title\": \"Sarah Vaughan and Clifford Brown\", \"artist\": \"Sarah Vaughan\"}" $ curl http://localhost:9090/albums [{"title":"Blue Train", "artist":"John Coltrane"}, {"title":"Jeru", "artist":"Gerry Mulligan"}, {"title":"Sarah Vaughan and Clifford Brown", "artist":"Sarah Vaughan"}] ================================================ FILE: examples/start-service-from-service-class-definition/start_service_from_service_class_definition.md ================================================ # Start service from service class definition The `service` keyword is used with the class definition to define a service class. A service object can be constructed from a service class. A service object supports network interaction with a remote system that is originated by the remote system. They are a special kind of object that contain `remote` and `resource` methods in addition to regular methods. The `Listener` object receives network input and makes calls to remote methods of attached service objects. The example below demonstrates how to start the service from its class definition. ::: code start_service_from_service_class_definition.bal ::: Run the service as follows. ::: out start_service_from_service_class_definition.server.out ::: Invoke the service by executing the following cURL command in a new terminal. ::: out start_service_from_service_class_definition.client.out ::: ## Related links - [Dynamic listener](/learn/by-example/dynamic-listener/) - [Object type inclusion](/learn/by-example/object-type-inclusion/) - [Client class](/learn/by-example/client-class/) ================================================ FILE: examples/start-service-from-service-class-definition/start_service_from_service_class_definition.metatags ================================================ description: This BBE demonstrates creating a listener object, starting a service from the service class definition, and attaching a listener to a service object in Ballerina. keywords: ballerina, ballerina by example, bbe, service class, listener, runtime, object type inclusion ================================================ FILE: examples/start-service-from-service-class-definition/start_service_from_service_class_definition.server.out ================================================ $ bal run start_service_from_service_class_definition.bal ================================================ FILE: examples/stop-handler/stop_handler.bal ================================================ import ballerina/http; import ballerina/io; import ballerina/lang.runtime; string[] fruitBasket = ["Apple", "Orange"]; function init() { io:println("initial items in fruit basket: " + fruitBasket.toString()); // Registers a function that will be called during the graceful shutdown. runtime:onGracefulStop(clearBasket); } public function clearBasket() returns error? { // Remove all elements from the array. fruitBasket.removeAll(); io:println("after removing all fruit items: " + fruitBasket.toString()); } service / on new http:Listener(9090) { resource function post addToBasket(@http:Payload string fruit) returns string[] { // Add an element to the array. fruitBasket.push(fruit); io:println("after adding a fruit item: " + fruitBasket.toString()); return fruitBasket; } } ================================================ FILE: examples/stop-handler/stop_handler.client.out ================================================ $ curl http://localhost:9090/addToBasket -d 'Guava' ["Apple", "Orange", "Guava"] ================================================ FILE: examples/stop-handler/stop_handler.md ================================================ # `StopHandler` A `StopHandler` is a function that is registered at runtime with a module and invoked during graceful shutdown. This example demonstrates how to register a `StopHandler` that will be executed at the end of the program. ::: code stop_handler.bal ::: Navigate to the directory that contains the 'stop_handler.bal' file, and run the 'bal run' command. ::: out stop_handler.server.out ::: Invoke the service by executing the following cURL command in a new terminal to add a fruit item. ::: out stop_handler.client.out ::: ================================================ FILE: examples/stop-handler/stop_handler.metatags ================================================ description: Registering a function that will be executed during the graceful shutdown. keywords: ballerina, ballerina by example, stop handler, bbe, service ================================================ FILE: examples/stop-handler/stop_handler.server.out ================================================ $ bal run stop_handler.bal initial items in fruit basket: ["Apple","Orange"] # Invoke the service using cURL command as mentioned below in a new terminal. after adding a fruit item: ["Apple","Orange","Guava"] # Send the interrupt signal SIGINT (Ctrl+C) to terminate the current process. after removing all fruit items: [] ================================================ FILE: examples/strands/strands.bal ================================================ import ballerina/io; function userSpeakerService(string userName) { string messagePrefix = userName + " speaking : "; foreach int i in 0...9 { io:println(messagePrefix, i); } } public function main() { io:println("In function worker"); // By default, named workers are multitasked cooperatively, not preemptively. // Each named worker has a strand (a logical thread of control) and // the execution switches between strands only at specific `yield` points. worker A { io:println("In worker A"); userSpeakerService("Worker A"); io:println("Worker A end"); } worker B { io:println("In worker B"); future<()> _ = start userSpeakerService("Worker B"); io:println("Worker B end"); } // Explicitly wait for named workers to complete. // This guarantees that the Ballerina program will not terminate till // the workers have completed their execution. wait A; wait B; } ================================================ FILE: examples/strands/strands.md ================================================ # Strands By default, named workers are multitasked cooperatively, not preemptively. Each named worker has a "strand" (logical thread of control) and execution switches between strands only at specific "yield" points such as doing a wait or when a library function invokes a system call that would block. ::: code strands.bal ::: ::: out strands.out ::: ================================================ FILE: examples/strands/strands.metatags ================================================ description: This BBE demonstrates Ballerina strands keywords: ballerina, ballerina by example, bbe, strands, concurrency ================================================ FILE: examples/strands/strands.out ================================================ $ bal run strands.bal In function worker In worker B Worker B end In worker A Worker A speaking : 0 Worker A speaking : 1 Worker B speaking : 0 Worker A speaking : 2 Worker A speaking : 3 Worker A speaking : 4 Worker B speaking : 1 Worker A speaking : 5 Worker B speaking : 2 Worker B speaking : 3 Worker A speaking : 6 Worker B speaking : 4 Worker A speaking : 7 Worker B speaking : 5 Worker A speaking : 8 Worker B speaking : 6 Worker A speaking : 9 Worker B speaking : 7 Worker A end Worker B speaking : 8 Worker B speaking : 9 ================================================ FILE: examples/stream-type/stream_type.bal ================================================ import ballerina/io; // Defines a class called `EvenNumberGenerator`, which implements the `next()` method. // This will be invoked when the `next()` method of the stream gets invoked. class EvenNumberGenerator { int i = 0; public isolated function next() returns record {|int value;|}|error? { self.i += 2; return {value: self.i}; } } public function main() { EvenNumberGenerator evenGen = new (); // Creates a `stream` passing an `EvenNumberGenerator` object to the `stream` constructor. stream evenNumberStream = new (evenGen); var evenNumber = evenNumberStream.next(); if (evenNumber !is error?) { io:println("Retrieved even number: ", evenNumber.value); } } ================================================ FILE: examples/stream-type/stream_type.md ================================================ # Stream type A stream represents a sequence of values that are generated as needed. The end of a stream is indicated with a termination value, which is error or nil. The type `stream` is a stream of which the members of the sequence are of the type `T`, and the termination value is type `E`. `stream` means `stream`. The stream type is a separate basic type, However like an object, it can be initialized with a `new` expression by passing an object, which implements the `next() returns record{| T value; |}|C` method where `C` is either `()`, `error` or `error?`. ::: code stream_type.bal ::: ::: out stream_type.out ::: ## Related links - [Create streams with query expression](/learn/by-example/create-streams-with-query) - [Querying with streams](/learn/by-example/querying-with-streams) ================================================ FILE: examples/stream-type/stream_type.metatags ================================================ description: This BBE demonstrates the `stream` type in Ballerina, creating a stream of values, loading values lazily, and saving resources by loading values when needed. keywords: ballerina, ballerina by example, bbe, stream type, streams, lazy, value, next ================================================ FILE: examples/stream-type/stream_type.out ================================================ $ bal run stream_type.bal Retrieved even number: 2 ================================================ FILE: examples/string-templates/string_templates.bal ================================================ import ballerina/io; public function main() { string word = "world"; string s0 = string `Hello, ${word}!`; io:println(s0); // Use interpolations to use escape characters. // Note how the first `\n` is treated as is. string s1 = string `line one \n rest of line 1 ${"\n"} second line`; io:println(s1); // Use interpolations to add the ` and $ characters. string s2 = string `Backtick: ${"`"} Dollar: ${"$"}`; io:println(s2); // A string template expression can be written on multiple lines by breaking at interpolations. string s3 = string `T_1 is ${ 1}, T_2 is ${1 + 2} and T_3 is ${1 + 2 + 3 }`; io:println(s3); // If there are no interpolations to break at a required point, you can introduce an interpolation with an empty // string. string s4 = string `prefix ${ ""}middle ${"" }suffix`; io:println(s4); } ================================================ FILE: examples/string-templates/string_templates.md ================================================ # String templates A string template is a template expression that can be used to create a string literal. It consists of the `string` tag and a sequence of characters interleaved with interpolations (in the form `${expression}`). Each interpolation must be a subtype of `boolean|int|float|decimal|string`. Every character not a part of the interpolation is interpreted as is to form a sequence of string literals broken at interpolations. This means if you want to add any escape characters you must add them as interpolations (for example `${"\n"}`). Every interpolation is converted to a string using the `toString` lang lib function and concatenated with the other string literals to form a single string literal. ::: code string_templates.bal ::: ::: out string_templates.out ::: ================================================ FILE: examples/string-templates/string_templates.metatags ================================================ description: This BBE demonstrates string templates in Ballerina. keywords: ballerina, ballerina by example, bbe, string templates ================================================ FILE: examples/string-templates/string_templates.out ================================================ $ bal run string_templates.bal Hello, world! line one \n rest of line 1 second line Backtick: ` Dollar: $ T_1 is 1, T_2 is 3 and T_3 is 6 prefix middle suffix ================================================ FILE: examples/strings/strings.bal ================================================ import ballerina/io; public function main() { // String literals use double quotes. You can use usual C escapes such as `\t \n`. // Numeric escapes specify Unicode code point using one or more hex digits `\u{H}`. string grin = "\u{1F600}"; // String concatenation uses `+` operator. string greeting = "Hello" + grin; io:println(greeting); // `greeting[1]` accesses character at index 1 (zero-based). io:println(greeting[1]); } ================================================ FILE: examples/strings/strings.md ================================================ # Strings The `string` type represents immutable sequence of zero or more Unicode characters. There is no separate character type: a character is represented by a `string` of length 1. Two `string` values are `==` if both sequences have the same characters. You can use `<`, `<=`, `>`, and `>=` operators on `string` values and they work by comparing code points. Unpaired surrogates are not allowed. ::: code strings.bal ::: ::: out strings.out ::: ================================================ FILE: examples/strings/strings.metatags ================================================ description: This BBE introduces the string type in Ballerina. keywords: ballerina, ballerina by example, bbe, string, unicode, utf-8, char ================================================ FILE: examples/strings/strings.out ================================================ $ bal run strings.bal Hello😀 e ================================================ FILE: examples/structural-typing/structural_typing.bal ================================================ import ballerina/io; public function main() { // 1 belongs to `int`. io:println(1 is int); // [10, 20, 30] belongs to `int[]`. io:println([10, 20, 30] is int[]); } ================================================ FILE: examples/structural-typing/structural_typing.md ================================================ # Structural typing Typing in Ballerina is structural: a type describes a set of values. Ballerina has semantic subtyping: subtype means subset. Universe of values is partitioned into basic types. Each value belongs to exactly one basic type. You can think of each value as being tagged with its basic type. There is a complexity in making structural typing work with mutation. - Immutable basic types (so far): `nil`, `boolean`, `int`, `float`, `string` - Mutable basic types (so far): `array`, `map`, and `record` ::: code structural_typing.bal ::: ::: out structural_typing.out ::: ================================================ FILE: examples/structural-typing/structural_typing.metatags ================================================ description: This BBE demonstrates structural typing in Ballerina. keywords: ballerina, ballerina by example, bbe, structural typing, types ================================================ FILE: examples/structural-typing/structural_typing.out ================================================ $ bal run structural_typing.bal true true ================================================ FILE: examples/structured-keys/structured_keys.bal ================================================ import ballerina/io; type Employee record { readonly record { string first; string last; } name; int salary; }; public function main() { //In the key field, the `name` is of the `record` type. table key(name) t = table [ {name: {first: "John", last: "Smith"}, salary: 100}, {name: {first: "Fred", last: "Bloggs"}, salary: 200} ]; Employee? e = t[{first: "Fred", last: "Bloggs"}]; io:println(e); record {|string first; string last;|} n = {first: "Sam", last: "Smith"}; // Make the key immutable using `cloneReadOnly()`. t.add({name: n.cloneReadOnly(), salary: 23}); io:println(t); } ================================================ FILE: examples/structured-keys/structured_keys.md ================================================ # Structured keys Key fields can be structured types as long as they belong to any subtype of plain data. Plain data is a simple value, a sequence value, or a structured value where all members are plain data. The value of the key field must be immutable. The initializer of the read-only field will be constructed as immutable. When using `add`/`put`, `cloneReadOnly()` can be used to create an immutable value. ::: code structured_keys.bal ::: ::: out structured_keys.out ::: ## Related links - [Table](/learn/by-example/table-syntax/) - [Multiple key fields](/learn/by-example/multiple-key-fields/) - [Records](/learn/by-example/records/) ================================================ FILE: examples/structured-keys/structured_keys.metatags ================================================ description: This BBE demonstrates how to set a record as key in table, create record key table, make immutable clone. keywords: ballerina, ballerina by example, bbe, structured keys, keys, table, map ================================================ FILE: examples/structured-keys/structured_keys.out ================================================ $ bal run structured_keys.bal {"name":{"first":"Fred","last":"Bloggs"},"salary":200} [{"name":{"first":"John","last":"Smith"},"salary":100},{"name":{"first":"Fred","last":"Bloggs"},"salary":200},{"name":{"first":"Sam","last":"Smith"},"salary":23}] ================================================ FILE: examples/synchronize-message-passing/synchronize_message_passing.bal ================================================ import ballerina/io; public function main() { worker A { int msg = 10; // Send a message synchronously to the `B` worker. // The 'A' worker will wait until 'B' receives the message. error? res = msg ->> B; io:println(res ?: "Transmission to B is successful"); // This transmission will not happen because the previous tranmission returns an error`. res = "Hello" ->> B; io:println(res ?: "Transmission to B is successful"); } worker B returns error? { int value; value = <- A; io:println(string `Received ${value} from the A worker.`); if value == 10 { return error("Error in the B worker."); } string text = <- A; io:println(string `Received ${text} from the A worker.`); } } ================================================ FILE: examples/synchronize-message-passing/synchronize_message_passing.md ================================================ # Synchronize message passing Use `->> W` to send a message synchronously to the `W` worker. These messages should be of the `anydata` type. Unlike asynchronous send, synchronous send will wait until the message is delivered. 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 synchronize_message_passing.bal ::: ::: out synchronize_message_passing.out ::: ================================================ FILE: examples/synchronize-message-passing/synchronize_message_passing.metatags ================================================ description: This BBE demonstrates how the `sync send` action can be used for concurrency in Ballerina. keywords: ballerina, ballerina by example, bbe, sync send, concurrency, worker ================================================ FILE: examples/synchronize-message-passing/synchronize_message_passing.out ================================================ $ bal run sync_send.bal Received 10 from the A worker. Transmission to B is successful error("Error in the B worker.") ================================================ FILE: examples/table/table.bal ================================================ import ballerina/io; type Employee record {| // The key must be read-only. readonly string name; int salary; |}; public function main() { // Creates a `table` with members of the `Employee` type in which each // member is uniquely identified using their `name` field. table key(name) employees = table [ { name: "John", salary: 100 }, { name: "Jane", salary: 200 } ]; // Since the key is already available, the `put` method updates the entry with the new `salary`. // If the key is not available, `put` will function the same as `add`. employees.put({name: "John", salary: 320}); io:println(employees); // As the key is not available, the `add` method appends the new entry. // If the key is available, the operation results in a panic. employees.add({name: "Sam", salary: 150}); io:println(employees); // Retrieves the `Employee` member with `"Fred"` as the value of the key. Employee? e = employees["Fred"]; io:println(e is ()); // Iterates over the rows of `employees` in the specified order. foreach Employee employee in employees { employee.salary += 102; } _ = employees.remove("John"); io:println(employees); } ================================================ FILE: examples/table/table.md ================================================ # Table The table constructor expression looks like an array constructor. The `add` and `put` langlib functions can be used to update a table. The `put` method will update the table if there is an entry with a given key or add a new entry otherwise, while the `add` method will panic if there is an entry with the given key. The `foreach` statement will iterate over a table's rows in their order. Use `t[k]` to access a row using its key. ::: code table.bal ::: ::: out table.out ::: ## Related links - [Maps](/learn/by-example/maps/) - [Structured keys](/learn/by-example/structured-keys/) - [Multiple key fields](/learn/by-example/multiple-key-fields/) - [Control openness](/learn/by-example/controlling-openness/) ================================================ FILE: examples/table/table.metatags ================================================ description: This BBE demonstrates how to create a table, create a table with a key, add entries to a table, update entries in a table, iterate over a table, get entries from a table, and create a table without a key in Ballerina. keywords: ballerina, ballerina by example, bbe, table syntax, table, map, key ================================================ FILE: examples/table/table.out ================================================ $ bal run table.bal [{"name":"John","salary":320},{"name":"Jane","salary":200}] [{"name":"John","salary":320},{"name":"Jane","salary":200},{"name":"Sam","salary":150}] true [{"name":"Jane","salary":302},{"name":"Sam","salary":252}] ================================================ FILE: examples/table-types/table_types.bal ================================================ import ballerina/io; type Employee record {| readonly string name; int salary; |}; public function main() { // Creates a `table` with members of the `Employee` type in which each // member is uniquely identified using their `name` field. table key(name) t1 = table [ { name: "John", salary: 100 }, { name: "Jane", salary: 200 } ]; io:println(t1); // A table can be declared without a key. table t2 = table [ { name: "John", salary: 100 }, { name: "Jane", salary: 200 } ]; io:println(t2); // A table can also be declared with a type parameter as the key type. table key t3 = table key(name) [ { name: "John", salary: 100 }, { name: "Jane", salary: 200 } ]; io:println(t3); } ================================================ FILE: examples/table-types/table_types.md ================================================ # Table types A `table` is a collection of records in which each record represents a row of the `table`. Each row of the `table` is uniquely identified by a key. This key must be immutable and each row’s key is stored in its fields. Compared to maps, - the key is part of the value rather than being separate. - the type of the key is not restricted to string. - the order of the members is preserved. ::: code table_types.bal ::: ::: out table_types.out ::: ## Related links - [Table](/learn/by-example/table/) - [Structured keys](/learn/by-example/structured-keys/) - [Multiple key fields](/learn/by-example/multiple-key-fields/) ================================================ FILE: examples/table-types/table_types.metatags ================================================ description: This BBE demonstrates how to create a table with a key, create a table without a key, and create a table with a key as a type-descriptor in Ballerina. keywords: ballerina, ballerina by example, bbe, table, map, key ================================================ FILE: examples/table-types/table_types.out ================================================ $ bal run table_types.bal [{"name":"John","salary":100},{"name":"Jane","salary":200}] [{"name":"John","salary":100},{"name":"Jane","salary":200}] [{"name":"John","salary":100},{"name":"Jane","salary":200}] ================================================ FILE: examples/task-frequency-job-execution/task_frequency_job_execution.bal ================================================ import ballerina/io; import ballerina/lang.runtime; import ballerina/task; // Creates a job to be executed by the scheduler. class Job { *task:Job; int i = 1; // Executes this function when the scheduled trigger fires. public function execute() { self.i += 1; io:println("MyCounter: ", self.i); } isolated function init(int i) { self.i = i; } } public function main() returns error? { // Schedules the task to execute the job every second. task:JobId id = check task:scheduleJobRecurByFrequency(new Job(0), 1); // Waits for nine seconds. runtime:sleep(9); // Unschedules the job. check task:unscheduleJob(id); } ================================================ FILE: examples/task-frequency-job-execution/task_frequency_job_execution.md ================================================ # Schedule job recurrence by frequency The `task` library provides an API to schedule Ballerina jobs periodically. For more information on the underlying module, see the [`task` module](https://lib.ballerina.io/ballerina/task/latest/). ::: code task_frequency_job_execution.bal ::: To run this sample, use the `bal run` command. ::: out task_frequency_job_execution.out ::: ================================================ FILE: examples/task-frequency-job-execution/task_frequency_job_execution.metatags ================================================ description: BBE on how to schedule the frequency job execution in Ballerina. keywords: ballerina, ballerina by examples, BBE, task, job, scheduler ================================================ FILE: examples/task-frequency-job-execution/task_frequency_job_execution.out ================================================ $ bal run task_frequency_job_execution.bal MyCounter: 1 MyCounter: 2 MyCounter: 3 MyCounter: 4 MyCounter: 5 MyCounter: 6 MyCounter: 7 MyCounter: 8 MyCounter: 9 MyCounter: 10 ================================================ FILE: examples/task-one-time-job-execution/task_one_time_job_execution.bal ================================================ import ballerina/io; import ballerina/lang.runtime; import ballerina/task; import ballerina/time; // Creates a job to be executed by the scheduler. class Job { *task:Job; int i = 1; // Executes this function when the scheduled trigger fires. public function execute() { self.i += 1; io:println("MyCounter: ", self.i); } isolated function init(int i) { self.i = i; } } public function main() returns error? { // Gets the current time. time:Utc currentUtc = time:utcNow(); // Increases the time by three seconds to get the specified time for scheduling the job. time:Utc newTime = time:utcAddSeconds(currentUtc, 3); // Gets the `time:Civil` for the given time. time:Civil time = time:utcToCivil(newTime); // Schedules the one-time job at the specified time. _ = check task:scheduleOneTimeJob(new Job(0), time); // Waits for five seconds. runtime:sleep(5); } ================================================ FILE: examples/task-one-time-job-execution/task_one_time_job_execution.md ================================================ # Schedule one time job The `task` library provides an API to schedule Ballerina jobs at a specific time. For more information on the underlying module, see the [`task` module](https://lib.ballerina.io/ballerina/task/latest/). ::: code task_one_time_job_execution.bal ::: To run this sample, use the `bal run` command. ::: out task_one_time_job_execution.out ::: ================================================ FILE: examples/task-one-time-job-execution/task_one_time_job_execution.metatags ================================================ description: BBE on how to schedule a one-time job execution in Ballerina. keywords: ballerina, ballerina by examples, BBE, task, job, scheduler ================================================ FILE: examples/task-one-time-job-execution/task_one_time_job_execution.out ================================================ $ bal run task_one_time_job_execution.bal MyCounter: 1 ================================================ FILE: examples/tcp-client/tcp_client.bal ================================================ import ballerina/io; import ballerina/tcp; public function main() returns error? { // Create a new TCP client by providing the `remoteHost` and `remotePort`. // Optionally, you can provide the interface that the socket needs to bind // and the timeout in seconds, which specifies the read timeout value. // tcp:Client client = check new ("localhost", 3000, localHost = "localhost", timeout = 5); tcp:Client socketClient = check new ("localhost", 9090); // Send the desired content to the server. check socketClient->writeBytes("Hello Ballerina".toBytes()); // Read the response from the server. readonly & byte[] receivedData = check socketClient->readBytes(); io:println("Received: ", string:fromBytes(receivedData)); // Close the connection between the server and the client. check socketClient->close(); } ================================================ FILE: examples/tcp-client/tcp_client.md ================================================ # TCP client - Send/Receive bytes The `tcp:Client` connects to a given TCP server socket, and then sends and receives byte streams. A `tcp:Client` is created by giving the IP and port number. Once connected, `writeBytes` and `readBytes` synchronous methods are used to send and receive byte streams. Since, they are synchronous methods often used in two different strands. Use this to interact with TCP servers or implement high level protocols based on TCP. ::: code tcp_client.bal ::: ## Prerequisites - Run the TCP service given in the [Send/Receive bytes](/learn/by-example/tcp-listener/) example. Run the client program by executing the command below. ::: out tcp_client.out ::: ## Related links - [`tcp:Client` client object - API documentation](https://lib.ballerina.io/ballerina/tcp/latest#Client) - [TCP Client - Specification](/spec/tcp/#4-client) - [Binary data](/learn/by-example/binary-data/) ================================================ FILE: examples/tcp-client/tcp_client.metatags ================================================ description: This Ballerina sample demonstrates how the TCP client interacts with the TCP listener service. keywords: ballerina, ballerina by example, bbe, socket, tcp ================================================ FILE: examples/tcp-client/tcp_client.out ================================================ $ bal run tcp_client.bal Received: Hello Ballerina ================================================ FILE: examples/tcp-client-ssl-tls/tcp_client_ssl_tls.bal ================================================ import ballerina/tcp; public function main() returns error? { // A TCP client can be configured to communicate through SSL/TLS as well. // To secure a client using SSL/TLS, the client needs to be configured with // a certificate file of the listener. // The `tcp:ClientSecureSocket` record provides the // SSL-related configurations of the client. tcp:Client securedClientEP = check new ("localhost", 9090, secureSocket = { cert: "../resource/path/to/public.crt" } ); check securedClientEP->writeBytes("Hello, World!".toBytes()); check securedClientEP->close(); } ================================================ FILE: examples/tcp-client-ssl-tls/tcp_client_ssl_tls.md ================================================ # TCP client - SSL/TLS The `tcp:Client` secured with SSL/TLS connects to a given SSL/TLS-secured TCP server socket, and then sends and receives byte streams. A `tcp:Client` secured with SSL/TLS is created by additionally giving `secureSocket` configurations which require the server's certificate as the `cert`. Once connected, `writeBytes` and `readBytes` synchronous methods are used to send and receive byte streams over an encrypted TLS connection. Use this to interact with TCP servers or implement high-level protocols based on TLS-encrypted secured TCP connection. ::: code tcp_client_ssl_tls.bal ::: ## Prerequisites - Run the TCP service given in the [SSL/TLS](/learn/by-example/tcp-service-ssl-tls/) example. Run the client program by executing the command below. ::: out tcp_client_ssl_tls.out ::: ## Related links - [`tcp` module - API documentation](https://lib.ballerina.io/ballerina/tcp/latest) - [TCP SSL/TLS - Specification](/spec/tcp/#512-configuring-tls-in-client-side) ================================================ FILE: examples/tcp-client-ssl-tls/tcp_client_ssl_tls.metatags ================================================ description: BBE on how to secure TCP client with SSL. keywords: ballerina, ballerina by example, bbe, ssl, tls, tcp client ================================================ FILE: examples/tcp-client-ssl-tls/tcp_client_ssl_tls.out ================================================ $ bal run tcp_client_ssl_tls.bal ================================================ FILE: examples/tcp-listener/tcp_listener.bal ================================================ import ballerina/io; import ballerina/log; import ballerina/tcp; // Bind the service to the port. service on new tcp:Listener(9090) { // This remote method is invoked when the new client connects to the server. remote function onConnect(tcp:Caller caller) returns tcp:ConnectionService { io:println("Client connected to echo server: ", caller.remotePort); return new EchoService(); } } service class EchoService { *tcp:ConnectionService; // This remote method is invoked once the content is received from the client. remote function onBytes(tcp:Caller caller, readonly & byte[] data) returns tcp:Error? { io:println("Echo: ", string:fromBytes(data)); // Echoes back the data to the client from which the data is received. check caller->writeBytes(data); } // This remote method is invoked in an erroneous situation, // which occurs during the execution of the `onConnect` or `onBytes` method. remote function onError(tcp:Error err) { log:printError("An error occurred", 'error = err); } // This remote method is invoked when the connection is closed. remote function onClose() { io:println("Client left"); } } ================================================ FILE: examples/tcp-listener/tcp_listener.md ================================================ # TCP service - Send/Receive bytes The `tcp:Service` allows opening up a TCP socket via a `tcp:Listener`. A `tcp:Listener` is created by giving the port number, to which `tcp:Service` is attached. The listener accepts and serves connections from TCP clients. The `onBytes` remote method is invoked once the content is received from the client. The `onError` and `onClose` remote methods get invoked in an erroneous situation and when the connection is closed respectively. Use a TCP service to establish connections and communicate over TCP protocol or implement high level protocols based on TCP. ::: code tcp_listener.bal ::: Run the service by executing the command below. ::: out tcp_listener.out ::: >**Tip:** You can invoke the above service via the [TCP client](/learn/by-example/tcp-client/). ## Related links - [`tcp` module - API documentation](https://lib.ballerina.io/ballerina/tcp/latest) - [TCP service - Specification](/spec/tcp/#3-service-types) ================================================ FILE: examples/tcp-listener/tcp_listener.metatags ================================================ description: This Ballerina sample demonstrates how the TCP listener service interacts with the TCP client. keywords: ballerina, ballerina by example, bbe, socket, tcp ================================================ FILE: examples/tcp-listener/tcp_listener.out ================================================ $ bal run tcp_listener.bal Client connected to echoServer: 48735 Echo: Hello Ballerina Client left ================================================ FILE: examples/tcp-service-ssl-tls/tcp_service_ssl_tls.bal ================================================ import ballerina/io; import ballerina/tcp; // An TCP listener can be configured to communicate through SSL/TLS as well. // To secure a listener using SSL/TLS, the listener needs to be configured with // a certificate file and a private key file for the listener. // The `tcp:ListenerSecureSocket` record provides the // SSL-related listener configurations of the listener. listener tcp:Listener securedEP = new (9090, secureSocket = { key: { certFile: "../resource/path/to/public.crt", keyFile: "../resource/path/to/private.key" } } ); service "tcp" on securedEP { remote function onConnect(tcp:Caller caller) returns tcp:ConnectionService { io:println("Client connected on server port: ", caller.remotePort); return new EchoService(); } } service class EchoService { *tcp:ConnectionService; remote function onBytes(readonly & byte[] data) returns byte[] { io:println("Received message: ", string:fromBytes(data)); return data; } } ================================================ FILE: examples/tcp-service-ssl-tls/tcp_service_ssl_tls.md ================================================ # TCP service - SSL/TLS The `tcp:Listener` configured with allows you to expose a connection secured with one-way SSL/TLS. A `tcp:Listener` secured with SSL/TLS is created by providing the `secureSocket` configurations which require the server's certificate as the `certFile` and the server's private key as the `keyFile`. Use this to interact with TCP clients or implement high-level protocols based on TLS-encrypted secured TCP connection. ::: code tcp_service_ssl_tls.bal ::: Run the service by executing the command below. ::: out tcp_service_ssl_tls.out ::: >**Tip:** You can invoke the above service via the [TCP SSL/TLS client](/learn/by-example/tcp-client-ssl-tls/). ## Related links - [`tcp` module - API documentation](https://lib.ballerina.io/ballerina/tcp/latest) - [TCP SSL/TLS - Specification](/spec/tcp/#511-configuring-tls-in-server-side) ================================================ FILE: examples/tcp-service-ssl-tls/tcp_service_ssl_tls.metatags ================================================ description: BBE on how to secure TCP listener with SSL. keywords: ballerina, ballerina by example, bbe, ssl, tls, tcp listener ================================================ FILE: examples/tcp-service-ssl-tls/tcp_service_ssl_tls.out ================================================ $ bal run tcp_service_ssl_tls.bal Client connected on server port: 5639 Received message: Hello, World! ================================================ FILE: examples/temp-files-directories/temp_files_directories.bal ================================================ import ballerina/file; import ballerina/io; public function main() returns error? { // Creates a temporary directory in the default `tmp` directory of the OS. string tmpDir = check file:createTempDir(); io:println("Absolute path of the tmp directory: ", tmpDir); // Creates a temporary file in the default `tmp` directory of the OS. string tmpResult = check file:createTemp(); io:println("Absolute path of the tmp file: ", tmpResult); // Creates a temporary file in a specific directory. string tmp2Result = check file:createTemp(dir = tmpDir); io:println("Absolute path of the tmp file: ", tmp2Result); } ================================================ FILE: examples/temp-files-directories/temp_files_directories.md ================================================ # Temp files and directories The `file` provides APIs to perform temp file and directory operations. For more information on the underlying module, see the [`file` module](https://lib.ballerina.io/ballerina/file/latest/). ::: code temp_files_directories.bal ::: To run this sample, use the `bal run` command. ::: out temp_files_directories.out ::: ================================================ FILE: examples/temp-files-directories/temp_files_directories.metatags ================================================ description: This BBE shows how to perform file-system operations in Ballerina. keywords: ballerina, ballerina by examples, BBE, file, path, temp ================================================ FILE: examples/temp-files-directories/temp_files_directories.out ================================================ $ bal run temp_files_directories.bal Absolute path of the tmp directory: /var/folders/f2/1s2f2mzd30ldl_fzxk4_gq3c0000gn/T/90eb0a6f-200a-454d-9285-f3264a71cd80 Absolute path of the tmp file: /var/folders/f2/1s2f2mzd30ldl_fzxk4_gq3c0000gn/T/9b0ce907-51cd-4f6b-98f0-d99566e3870d Absolute path of the tmp file: /var/folders/f2/1s2f2mzd30ldl_fzxk4_gq3c0000gn/T/90eb0a6f-200a-454d-9285-f3264a71cd80/14c45332-37df-44d1-b24b-3dbcb7c7404c ================================================ FILE: examples/testerina-assertions/testerina_assertions.bal ================================================ import ballerina/test; @test:Config { } function testAssertEquals() { json a = {name:"John Doe", age:25, address:{city:"Colombo", country:"Sri Lanka"}}; json b = {name:"John Doe", age:25, address:{city:"Colombo", country:"Sri Lanka"}}; // Asserts two values for value equality using `assertEquals`. test:assertEquals(a, b, msg = "JSON values are not equal"); } @test:Config { } function testAssertNotEquals() { string s1 = "abc"; string s2 = "def"; // Asserts two values for value inequality using `assertNotEquals`. test:assertNotEquals(s1, s2, msg = "String values are equal"); } @test:Config { } function testAssertTrue() { boolean value = true; // Asserts if the provided value is `true`. test:assertTrue(value, msg = "AssertTrue failed"); } @test:Config { } function testAssertFalse() { boolean value = false; // Asserts if the provided value is `false`. test:assertFalse(value, msg = "AssertFalse failed"); } @test:Config { } function testAssertFail() { boolean flag = true; if (flag) { return; } // Intentionally, throws an `AssertionError`. test:assertFail(msg = "AssertFailed"); } class Person { public string name = ""; public int age = 0; public Person? parent = (); private string email = "default@abc.com"; string address = "No 20, Palm grove"; } @test:Config { } function testAssertExactEquals() { Person person1 = new; Person person2 = person1; // Compares the values for exact equality i.e. whether both refer to the same entity. test:assertExactEquals(person1, person2, msg = "Objects are not exactly equal"); } @test:Config { } function testAssertNotExactEquals() { Person person1 = new; Person person2 = new; // Compares the values for the negation of exact equality. test:assertNotExactEquals(person1, person2, msg = "Objects are exactly equal"); } ================================================ FILE: examples/testerina-assertions/testerina_assertions.md ================================================ # Assertions Testerina has in-built assertions that enable you to assert an outcome against an expected outcome. This example illustrates how to use different assertions. For more information, see [Testing Ballerina Code](https://ballerina.io/learn/test-ballerina-code/write-tests/#use-assertions) and the [`test` module](https://lib.ballerina.io/ballerina/test/latest/). ::: code testerina_assertions.bal ::: ::: out testerina_assertions.out ::: ================================================ FILE: examples/testerina-assertions/testerina_assertions.metatags ================================================ description: BBE on how to use assertions for unit testing. keywords: ballerina, programming language, testing, assertion ================================================ FILE: examples/testerina-assertions/testerina_assertions.out ================================================ $ bal test test_module Compiling source ballerinatest/test_module:0.1.0 Running tests ballerinatest/test_module:0.1.0 [pass] testAssertEquals [pass] testAssertExactEquals [pass] testAssertFail [pass] testAssertFalse [pass] testAssertNotEquals [pass] testAssertNotExactEquals [pass] testAssertTrue 7 passing 0 failing 0 skipped ================================================ FILE: examples/testerina-before-and-after-each/testerina_before_and_after_each.bal ================================================ import ballerina/io; import ballerina/test; // The `BeforeEach` function is executed before each test function. @test:BeforeEach function beforeEachFunc() { io:println("I'm the before each function!"); } // The `AfterEach` function is executed after each test function. @test:AfterEach function afterEachFunc() { io:println("I'm the after each function!"); } // A test function. @test:Config { } function testFunction1() { io:println("I'm in test function 1!"); test:assertTrue(true, msg = "Failed!"); } // Another test function. @test:Config { } function testFunction2() { io:println("I'm in test function 2!"); test:assertTrue(true, msg = "Failed!"); } ================================================ FILE: examples/testerina-before-and-after-each/testerina_before_and_after_each.md ================================================ # Before and after each The function specified with the `BeforeEach` annotation is executed before every test and the function specified with the `AfterEach` annotation is executed after every test within the test suite. This can be used for repeatedly initializing and tearing down test level aspects before every test function. For more information, see [Testing Ballerina Code](https://ballerina.io/learn/test-ballerina-code/execute-tests/#understand-the-test-execution-behavior) and the [`test` module](https://lib.ballerina.io/ballerina/test/latest/). ::: code testerina_before_and_after_each.bal ::: ::: out testerina_before_and_after_each.out ::: ================================================ FILE: examples/testerina-before-and-after-each/testerina_before_and_after_each.metatags ================================================ description: BBE on how to use before and after each test configurations. keywords: ballerina, programming language, testing, before each test, after each test ================================================ FILE: examples/testerina-before-and-after-each/testerina_before_and_after_each.out ================================================ $ bal test test_module Compiling source ballerinatest/test_module:0.1.0 Running tests ballerinatest/test_module:0.1.0 I'm the before each function! I'm in test function 1! I'm the after each function! I'm the before each function! I'm in test function 2! I'm the after each function! [pass] testFunction1 [pass] testFunction2 2 passing 0 failing 0 skipped ================================================ FILE: examples/testerina-before-and-after-groups/testerina_before_and_after_groups.bal ================================================ import ballerina/io; import ballerina/test; // Executed before the first test function of the group `g1`. @test:BeforeGroups { value:["g1"] } function beforeGroupsFunc() { io:println("I'm the before groups function!"); } // Executed after the last test function of the group `g1`. @test:AfterGroups { value:["g1"] } function afterGroupsFunc() { io:println("I'm the after groups function!"); } // A test function belonging to the group `g1`. @test:Config { groups: ["g1"]} function testFunction1() { io:println("I'm in test function 1!"); test:assertTrue(true, msg = "Failed!"); } // Another test function. @test:Config {} function testFunction2() { io:println("I'm in test function 2!"); test:assertTrue(true, msg = "Failed!"); } ================================================ FILE: examples/testerina-before-and-after-groups/testerina_before_and_after_groups.md ================================================ # Before and after groups The function specified with the `BeforeGroups` annotation is executed once before all the tests belonging to the specified group is executed and the function specified with the `AfterGroups` annotation is executed once after all the tests belonging to the specified group is executed. For more information, see [Testing Ballerina Code](https://ballerina.io/learn/test-ballerina-code/execute-tests/#understand-the-test-execution-behavior) and the [`test` module](https://lib.ballerina.io/ballerina/test/latest/). ::: code testerina_before_and_after_groups.bal ::: ::: out testerina_before_and_after_groups.out ::: ================================================ FILE: examples/testerina-before-and-after-groups/testerina_before_and_after_groups.metatags ================================================ description: BBE on how to use before and after groups configurations. keywords: ballerina, programming language, testing, before groups, after groups ================================================ FILE: examples/testerina-before-and-after-groups/testerina_before_and_after_groups.out ================================================ $ bal test test_module Compiling source ballerinatest/test_module:0.1.0 Running tests ballerinatest/test_module:0.1.0 I'm the before groups function! I'm in test function 1! I'm the after groups function! I'm in test function 2! [pass] testFunction1 [pass] testFunction2 2 passing 0 failing 0 skipped ================================================ FILE: examples/testerina-before-and-after-suite/testerina_before_and_after_suite.bal ================================================ import ballerina/io; import ballerina/test; // Executed before all the test functions in the module. @test:BeforeSuite function beforeSuit() { io:println("I'm the before suite function!"); } // A Test function. @test:Config { } function testFunction1() { io:println("I'm in test function 1!"); test:assertTrue(true, msg = "Failed"); } // A Test function. @test:Config { } function testFunction2() { io:println("I'm in test function 2!"); test:assertTrue(true, msg = "Failed"); } // Executed after all the test functions in the module. @test:AfterSuite { } function afterSuite() { io:println("I'm the after suite function!"); } ================================================ FILE: examples/testerina-before-and-after-suite/testerina_before_and_after_suite.md ================================================ # Before and after suite The `BeforeSuite` annotation allows you to execute a function before executing a test suite. Similarly, the `AfterSuite` annotation can be used to execute a function after a test suite. A module is considered as a suite in the Test framework. These annotations can be used to set up prerequisites and post actions for a test suite. For more information, see [Testing Ballerina Code](https://ballerina.io/learn/test-ballerina-code/execute-tests/#understand-the-test-execution-behavior) and the [`test` module](https://lib.ballerina.io/ballerina/test/latest/). ::: code testerina_before_and_after_suite.bal ::: ::: out testerina_before_and_after_suite.out ::: ================================================ FILE: examples/testerina-before-and-after-suite/testerina_before_and_after_suite.metatags ================================================ description: BBE on how to use before and after suite configurations. keywords: ballerina, programming language, testing, before suite, after suite ================================================ FILE: examples/testerina-before-and-after-suite/testerina_before_and_after_suite.out ================================================ $ bal test test_module Compiling source ballerinatest/test_module:0.1.0 Running tests ballerinatest/test_module:0.1.0 I'm the before suite function! I'm in test function 1! I'm in test function 2! I'm the after suite function! [pass] testFunction1 [pass] testFunction2 2 passing 0 failing 0 skipped ================================================ FILE: examples/testerina-before-and-after-test/testerina_before_and_after_test.bal ================================================ import ballerina/io; import ballerina/test; // Executed before the `testFunction` function. function beforeFunc() { io:println("I'm the before function!"); } // The Test function. // The `before` and `after` attributes are used to define the functions // that need to be executed before and after this test function. @test:Config { before: beforeFunc, after: afterFunc } function testFunction() { io:println("I'm in test function!"); test:assertTrue(true, msg = "Failed!"); } // Executed after the `testFunction` function. function afterFunc() { io:println("I'm the after function!"); } ================================================ FILE: examples/testerina-before-and-after-test/testerina_before_and_after_test.md ================================================ # Before and after test The `before` attribute allows you to execute a function before a test function. Similarly, the `after` attribute can be used to execute a function after a test function. These annotations can be used to set up the prerequisites and post actions for a test case. For more information, see [Testing Ballerina Code](https://ballerina.io/learn/test-ballerina-code/execute-tests/#understand-the-test-execution-behavior) and the [`test` module](https://lib.ballerina.io/ballerina/test/latest/). ::: code testerina_before_and_after_test.bal ::: ::: out testerina_before_and_after_test.out ::: ================================================ FILE: examples/testerina-before-and-after-test/testerina_before_and_after_test.metatags ================================================ description: BBE on how to use before and after test configurations. keywords: ballerina, programming language, testing, before test, after test ================================================ FILE: examples/testerina-before-and-after-test/testerina_before_and_after_test.out ================================================ $ bal test test_module Compiling source ballerinatest/test_module:0.1.0 Running tests ballerinatest/test_module:0.1.0 I'm the before function! I'm in test function! I'm the after function! [pass] testFunction 1 passing 0 failing 0 skipped ================================================ FILE: examples/testerina-data-driven-tests/testerina_data_driven_tests.bal ================================================ import ballerina/io; import ballerina/test; // The `dataProvider` attribute is used to specify a data-provider function for the test. @test:Config { dataProvider: stringDataProvider } // Data is passed to the function as function parameters. function testAddingValues(string fValue, string sValue, string result) returns error? { int value1 = check 'int:fromString(fValue); int value2 = check 'int:fromString(sValue); int result1 = check 'int:fromString(result); io:println("Input : [" + fValue + "," + sValue + "," + result + "]"); test:assertEquals(value1 + value2, result1, msg = "Incorrect Sum"); } // The data provider function, which returns a `string` value-set in array format. function stringDataProvider() returns (string[][]) { return [["1", "2", "3"], ["10", "20", "30"], ["5", "6", "11"]]; } @test:Config { dataProvider: mapDataProvider } function mapDataProviderTest(int value1, int value2, string fruit) returns error? { io:println("Input : [" + value1.toBalString() + "," + value2.toBalString() + "," + fruit + "]"); test:assertEquals(value1, value2, msg = "The provided values are not equal"); test:assertEquals(fruit.length(), 6); } // The data provider function, which returns a data set as a map of tuples. function mapDataProvider() returns map<[int, int, string]>|error { map<[int, int, string]> dataSet = { "banana": [10, 10, "banana"], "cherry": [5, 5, "cherry"] }; return dataSet; } ================================================ FILE: examples/testerina-data-driven-tests/testerina_data_driven_tests.md ================================================ # Data-driven tests The Ballerina Test framework provides in-built support for data-driven tests. You can specify a function that returns a set of data values as a data-provider to a test function. The test execution will iterate the same test function over the provided dataset. For more information, see [Testing Ballerina Code](https://ballerina.io/learn/test-ballerina-code/define-data-driven-tests/) and the [`test` module](https://lib.ballerina.io/ballerina/test/latest/). ::: code testerina_data_driven_tests.bal ::: ::: out testerina_data_driven_tests.out ::: ================================================ FILE: examples/testerina-data-driven-tests/testerina_data_driven_tests.metadata ================================================ description: BBE on how to setup data-providers for data-driven tests. keywords: ballerina, programming language, testing, data-driven tests, data-provider ================================================ FILE: examples/testerina-data-driven-tests/testerina_data_driven_tests.out ================================================ $ bal test test_module Compiling source ballerinatest/test_module:0.1.0 Running tests ballerinatest/test_module:0.1.0 Input : [10,10,banana] Input : [5,5,cherry] Input : [1,2,3] Input : [10,20,30] Input : [5,6,11] [pass] mapDataProviderTest#banana [pass] mapDataProviderTest#cherry [pass] testAddingValues#0 [pass] testAddingValues#1 [pass] testAddingValues#2 5 passing 0 failing 0 skipped ================================================ FILE: examples/testerina-group-tests/testerina_group_disable_groups.out ================================================ $ bal test --disable-groups g2 testerina_group_tests.bal Compiling source testerina_group_tests.bal Running tests testerina_group_tests.bal I'm in test belonging to group g1! I'm the ungrouped test [pass] testFunction1 [pass] testFunction3 2 passing 0 failing 0 skipped ================================================ FILE: examples/testerina-group-tests/testerina_group_tests.bal ================================================ import ballerina/io; import ballerina/test; // Belongs to the test group `g1`. @test:Config { groups: ["g1"] } function testFunction1() { io:println("I'm in test belonging to group g1!"); test:assertTrue(true, msg = "Failed!"); } // Belongs to the test groups `g1` and `g2`. @test:Config { groups: ["g1", "g2"] } function testFunction2() { io:println("I'm in test belonging to groups g1 and g2!"); test:assertTrue(true, msg = "Failed!"); } // Does not belong to any test group. @test:Config { } function testFunction3() { io:println("I'm the ungrouped test"); test:assertTrue(true, msg = "Failed!"); } ================================================ FILE: examples/testerina-group-tests/testerina_group_tests.md ================================================ # Group tests You can tag your test cases with a single group name or multiple group names (one or more). This allows you to control the execution of selected tests. In order to execute tests belonging to a selected test group, you can name the test groups that are to be executed when you run the tests. Likewise, you can exclude executing the selected test groups as well. For more information, see [Testing Ballerina Code](https://ballerina.io/learn/test-ballerina-code/define-test-groups/) and the [`test` module](https://lib.ballerina.io/ballerina/test/latest/). ::: code testerina_group_tests.bal ::: Run the tests belonging to the `g1` and `g2` groups. ::: out testerina_group_tests_groups_g1_g2.out ::: Run the tests belonging to the `g1` group. ::: out testerina_group_tests_groups_g1.out ::: Run all tests other than the tests belonging to the `g2` group. ::: out testerina_group_disable_groups.out ::: ================================================ FILE: examples/testerina-group-tests/testerina_group_tests.metadata ================================================ description: BBE on how to group test cases. keywords: ballerina, programming language, testing, grouping tests ================================================ FILE: examples/testerina-group-tests/testerina_group_tests_groups_g1.out ================================================ $ bal test --groups g1 testerina_group_tests.bal Compiling source testerina_group_tests.bal Running tests testerina_group_tests.bal I'm in test belonging to group g1! I'm in test belonging to groups g1 and g2! [pass] testFunction1 [pass] testFunction2 2 passing 0 failing 0 skipped ================================================ FILE: examples/testerina-group-tests/testerina_group_tests_groups_g1_g2.out ================================================ $ bal test --groups g1,g2 testerina_group_tests.bal Compiling source testerina_group_tests.bal Running tests testerina_group_tests.bal I'm in test belonging to group g1! I'm in test belonging to groups g1 and g2! [pass] testFunction1 [pass] testFunction2 2 passing 0 failing 0 skipped ================================================ FILE: examples/testerina-guarantee-test-execution-order/testerina_guarantee_test_execution_order.bal ================================================ import ballerina/io; import ballerina/test; // This test function depends on the `testFunction3`. @test:Config { dependsOn: [testFunction3] } function testFunction1() { io:println("I'm in test function 1!"); test:assertTrue(true, msg = "Failed!"); } // This test function depends on the `testFunction1`. @test:Config { dependsOn: [testFunction1] } function testFunction2() { io:println("I'm in test function 2!"); test:assertTrue(true, msg = "Failed!"); } // This will be executed without depending on other functions. // However, since other functions depend on this function, it will be executed first. @test:Config { } function testFunction3() { io:println("I'm in test function 3!"); test:assertTrue(true, msg = "Failed!"); } ================================================ FILE: examples/testerina-guarantee-test-execution-order/testerina_guarantee_test_execution_order.md ================================================ # Guarantee test execution order The `dependsOn` attribute can be used to define a list of functions that the test function depends on. These functions will be executed before the execution of that test. This allows you to ensure that the tests are being executed in the expected order. For more information, see [Testing Ballerina Code](https://ballerina.io/learn/test-ballerina-code/test-a-simple-function/) and the [`test` module](https://lib.ballerina.io/ballerina/test/latest/). ::: code testerina_guarantee_test_execution_order.bal ::: ::: out testerina_guarantee_test_execution_order.out ::: ================================================ FILE: examples/testerina-guarantee-test-execution-order/testerina_guarantee_test_execution_order.metadata ================================================ description: BBE on how to setup a test execution order when unit testing. keywords: ballerina, programming language, testing, execution order ================================================ FILE: examples/testerina-guarantee-test-execution-order/testerina_guarantee_test_execution_order.out ================================================ $ bal test test_module Compiling source ballerinatest/test_module:0.1.0 Running tests ballerinatest/test_module:0.1.0 I'm in test function 3! I'm in test function 1! I'm in test function 2! [pass] testFunction3 [pass] testFunction1 [pass] testFunction2 3 passing 0 failing 0 skipped ================================================ FILE: examples/testerina-mocking-functions/testerina_mocking_functions.md ================================================ # Function mocking Mock functions allow you to hide the real function implementation and engage your own definition when running tests. This allows you to isolate your tests from the other modules and functions. Function mocks can be stubbed with return values or with another user-defined function, which has the same signature as the original function. For more information, see [Testing Ballerina Code](https://ballerina.io/learn/test-ballerina-code/mocking/#mock-functions) and the [`test` module](https://lib.ballerina.io/ballerina/test/latest/). ::: code testerina_mocking_functions_main.bal ::: ::: code testerina_mocking_functions_test.bal ::: ::: out testerina_mocking_functions_test.out ::: ================================================ FILE: examples/testerina-mocking-functions/testerina_mocking_functions.metatags ================================================ description: BBE on how to mock functions for unit testing. keywords: ballerina, programming language, testing, mocking ================================================ FILE: examples/testerina-mocking-functions/testerina_mocking_functions_main.bal ================================================ // Calls the `intAdd` function and returns the result. public function addValues(int a, int b) returns int { return intAdd(a, b); } // Adds two integers and returns the result. public function intAdd(int a, int b) returns int { return (a + b); } ================================================ FILE: examples/testerina-mocking-functions/testerina_mocking_functions_test.bal ================================================ // This demonstrates different ways to mock functions. import ballerina/test; import ballerina/io; // Creates a `MockFunction` for stubbing calls to // the `intAdd` function of the same module. @test:Mock { functionName: "intAdd" } test:MockFunction intAddMockFn = new(); @test:Config {} function testReturn() { // Stubs the calls to return a specific value. test:when(intAddMockFn).thenReturn(20); // Stubs the calls to return a specific value when // specific arguments are provided. test:when(intAddMockFn).withArguments(0, 0).thenReturn(-1); test:assertEquals(intAdd(10, 6), 20, msg = "function mocking failed"); test:assertEquals(intAdd(0, 0), -1, msg = "function mocking with arguments failed"); } // Creates a `MockFunction` that should replace the // imported `io:println` function. @test:Mock { moduleName: "ballerina/io", functionName: "println" } test:MockFunction printlnMockFn = new(); int tally = 0; // This has a function signature similar to the `io:println` function. public function mockPrint(any|error... val) { tally = tally + 1; } @test:Config {} function testCall() { // Stubs the calls to the `io:println` function // to invoke the `mockPrint` function instead. test:when(printlnMockFn).call("mockPrint"); io:println("Testing 1"); io:println("Testing 2"); io:println("Testing 3"); test:assertEquals(tally, 3); } ================================================ FILE: examples/testerina-mocking-functions/testerina_mocking_functions_test.out ================================================ $ bal test bbe_mocking Compiling source ballerinatest/bbe_mocking:0.1.0 Running Tests bbe_mocking [pass] testCall [pass] testReturn 2 passing 0 failing 0 skipped ================================================ FILE: examples/testerina-mocking-objects/testerina_mocking_objects.md ================================================ # Object mocking Object mocking enables controlling the values of member variables and the behavior of the member functions of an object. Mocking of objects can be done in two ways. 1. Creating a test double - providing an equivalent mock object in place of the real 2. Stubbing the member function or member variable - stubbing the behavior of functions and values of variables 3. Creating a test double is suitable when a single mock function/object can be used throughout all tests whereas stubbing is ideal when defining different behaviors for different test cases is required. For more information, see [Testing Ballerina Code](https://ballerina.io/learn/test-ballerina-code/mocking/#mock-objects) and the [`test` module](https://lib.ballerina.io/ballerina/test/latest/). ::: code testerina_mocking_objects_main.bal ::: ::: code testerina_mocking_objects_test.bal ::: ::: out testerina_mocking_objects_test.out ::: ================================================ FILE: examples/testerina-mocking-objects/testerina_mocking_objects.metatags ================================================ description: BBE on how to mock objects for unit testing. keywords: ballerina, programming language, testing, mocking ================================================ FILE: examples/testerina-mocking-objects/testerina_mocking_objects_main.bal ================================================ import ballerina/http; import ballerina/io; import ballerina/email; // Sample client that you can use for member access. public client class ExampleClient { public string id; public function init(string id) { self.id = id; } } // Client objects are defined globally to be able to replace in the test files. http:Client clientEndpoint = check new("http://postman-echo.com"); email:SmtpClient smtpClient = check new("localhost", "admin", "admin"); ExampleClient exampleClient = new("originalId"); // Performs two `GET` requests to the specified // endpoint and returns the response. function performGet() returns @tainted http:Response { io:println("Executing the 1st GET request"); http:Response response = checkpanic clientEndpoint -> get("/headers"); io:println("Status code: ", response.statusCode.toString()); if (response.statusCode == 200) { io:println("Executing the 2nd GET request"); response = checkpanic clientEndpoint -> get("/get?test=123", {"Sample-Name": "http-client-connector"}); io:println("Status code: ", response.statusCode.toString()); } return response; } // Sends an email to the specified email addresses // and returns an error if found. function sendNotification(string[] emailIds) returns error? { email:Message msg = { 'from: "builder@abc.com", subject: "Error Alert ...", to: emailIds, body: "" }; return smtpClient -> sendMessage(msg); } ================================================ FILE: examples/testerina-mocking-objects/testerina_mocking_objects_test.bal ================================================ // This demonstrates different ways to mock a client object. import ballerina/test; import ballerina/http; import ballerina/email; // The test double of the `http:Client` object with the // implementation of the required functions. public client class MockHttpClient { remote isolated function get(@untainted string path, map? headers = (), http:TargetType targetType = http:Response) returns http:Response | anydata | http:ClientError { http:Response response = new; response.statusCode = 500; return response; } } @test:Config { } function testTestDouble() { // Creates and assigns the defined test-double. clientEndpoint = test:mock(http:Client, new MockHttpClient()); http:Response res = performGet(); test:assertEquals(res.statusCode, 500); } @test:Config { } function testReturn() { // Creates and assigns a default mock object, // which subsequently needs to be stubbed. clientEndpoint = test:mock(http:Client); // Stubs the `get` function to return the specified HTTP response. test:prepare(clientEndpoint).when("get").thenReturn(new http:Response()); http:Response res = performGet(); test:assertEquals(res.statusCode, 200); } @test:Config { } function testReturnSequence() { http:Response mockResponse = new; mockResponse.statusCode = 404; clientEndpoint = test:mock(http:Client); // Stubs the `get` function to return the specified HTTP response // for each call (i.e., The first call will return the status code `200` // and the second call will return the status code `404`). test:prepare(clientEndpoint).when("get").thenReturnSequence( new http:Response(), mockResponse); http:Response res = performGet(); test:assertEquals(res.statusCode, 404); } @test:Config { } function testReturnWithArgs() { http:Response mockResponse = new; mockResponse.statusCode = 404; clientEndpoint = test:mock(http:Client); // This stubs the `get` function to return the specified HTTP response when the specified // argument is passed. test:prepare(clientEndpoint).when("get"). withArguments("/headers").thenReturn(mockResponse); // The object and record types should be denoted by the `test:ANY` constant. test:prepare(clientEndpoint).when("get").withArguments("/get?test=123") .thenReturn(mockResponse); http:Response res = performGet(); test:assertEquals(res.statusCode, 404); } @test:Config { } function testSendNotification() { smtpClient = test:mock(email:SmtpClient); // Stubs the `send` method of the `mockSmtpClient` to do nothing. // This is used for functions with an optional or no return type. test:prepare(smtpClient).when("sendMessage").doNothing(); string[] emailIds = ["user1@test.com", "user2@test.com"]; error? err = sendNotification(emailIds); test:assertEquals(err, ()); } @test:Config {} function testMemberVariable() { string mockId = "test"; lock { exampleClient = test:mock(ExampleClient); // Stubs the value of the `id` to return the specified string. test:prepare(exampleClient).getMember("id").thenReturn(mockId); test:assertEquals(exampleClient.id, mockId); } } ================================================ FILE: examples/testerina-mocking-objects/testerina_mocking_objects_test.out ================================================ $ bal test bbe_mocking Compiling source ballerinatest/bbe_mocking:0.1.0 Running Tests bbe_mocking Executing the 1st GET request Status code: 200 Executing the 2nd GET request Status code: 200 Executing the 1st GET request Status code: 200 Executing the 2nd GET request Status code: 404 Executing the 1st GET request Status code: 404 Executing the 1st GET request Status code: 500 [pass] testMemberVariable [pass] testReturn [pass] testReturnSequence [pass] testReturnWithArgs [pass] testSendNotification [pass] testTestDouble 6 passing 0 failing 0 skipped ================================================ FILE: examples/time-formatting-and-parsing/tests/time_formatting_and_parsing_test.bal ================================================ import ballerina/io; import ballerina/test; string[] outputs = []; int counter = 0; // 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(io:Printable... s) { string outstr = ""; foreach io:Printable str in s { outstr = outstr + new PrintableClassImpl(str).toString(); } outputs[counter] = outstr; counter += 1; } // From ballerina/io module to mock io:println() with raw templates class PrintableClassImpl { io:Printable printable; public isolated function init(io:Printable printable) { self.printable = printable; } public isolated function toString() returns string { io:Printable printable = self.printable; if printable is io:PrintableRawTemplate { return new PrintableRawTemplateImpl(printable).toString(); } else if printable is error { return printable.toString(); } else { return printable.toString(); } } } class PrintableRawTemplateImpl { *object:RawTemplate; public io:Printable[] insertions; public isolated function init(io:PrintableRawTemplate printableRawTemplate) { self.strings = printableRawTemplate.strings; self.insertions = printableRawTemplate.insertions; } public isolated function toString() returns string { io:Printable[] templeteInsertions = self.insertions; string[] templeteStrings = self.strings; string templatedString = templeteStrings[0]; foreach int i in 1 ..< (templeteStrings.length()) { io:Printable templateInsert = templeteInsertions[i - 1]; if (templateInsert is io:PrintableRawTemplate) { templatedString += new PrintableRawTemplateImpl(templateInsert).toString() + templeteStrings[i]; } else if (templateInsert is error) { templatedString += templateInsert.toString() + templeteStrings[i]; } else { templatedString += templateInsert.toString() + templeteStrings[i]; } } return templatedString; } } @test:Config {} function testFunc() returns error? { test:when(mock_printLn).call("mockPrint"); // Invoking the main function check main(); test:assertEquals(outputs[0], "UTC value: [1196676930,0.12]"); test:assertEquals(outputs[1], "UTC string representation: 2007-12-03T10:15:30.120Z"); test:assertEquals(outputs[2], "Converted civil value: {\"utcOffset\":{\"hours\":5,\"minutes\":30},\"timeAbbrev\":\"Asia/Colombo\",\"dayOfWeek\":1,\"year\":2021,\"month\":4,\"day\":12,\"hour\":23,\"minute\":20,\"second\":50.52}"); test:assertEquals(outputs[3], "Civil string representation: 2021-04-12T23:20:50.520+05:30"); test:assertEquals(outputs[4], "Email formatted string: Mon, 3 Dec 2007 10:15:30 Z"); test:assertEquals(outputs[5], "Civil record of the email string: {\"utcOffset\":{\"hours\":-8,\"minutes\":0},\"timeAbbrev\":\"America/Los_Angeles\",\"dayOfWeek\":3,\"year\":2021,\"month\":3,\"day\":10,\"hour\":19,\"minute\":51,\"second\":55}"); test:assertEquals(outputs[6], "Email string of the civil record: Wed, 10 Mar 2021 19:51:55 -0800"); } ================================================ FILE: examples/time-formatting-and-parsing/time_formatting_and_parsing.bal ================================================ import ballerina/io; import ballerina/time; public function main() returns error? { // Converts a given RFC 3339 timestamp // (e.g. `2007-12-03T10:15:30.12Z`) string to a `time:Utc` value. time:Utc utc = check time:utcFromString("2007-12-03T10:15:30.120Z"); io:println("UTC value: " + utc.toString()); // Converts a given `time:Utc` to a RFC 3339 timestamp // (e.g. `2007-12-03T10:15:30.00Z`) string. string utcString = time:utcToString(utc); io:println(`UTC string representation: ${utcString}`); // Converts a given RFC 3339 timestamp(e.g. `2007-12-03T10:15:30.00Z`) // to a `time:Civil` record. time:Civil civil1 = check time:civilFromString("2021-04-12T23:20:50.520+05:30[Asia/Colombo]"); io:println("Converted civil value: " + civil1.toString()); // Converts a given `time:Civil` value to a RFC 3339 // (e.g. `2021-04-12T23:20:50.520+05:30`) formatted string. string civilString = check time:civilToString(civil1); io:println(`Civil string representation: ${civilString}`); // Converts a given UTC to an RFC 5322 formatted string // (e.g `Mon, 3 Dec 2007 10:15:30 GMT`). string emailFormattedString = time:utcToEmailString(utc, "Z"); io:println(`Email formatted string: ${emailFormattedString}`); // Converts a given RFC 5322 formatted string // (e.g `Mon, 3 Dec 2007 10:15:30 GMT`) to a `time:Civil` record. time:Civil civil2 = check time:civilFromEmailString("Wed, 10 Mar 2021 19:51:55 -0800 (PST)"); io:println(`Civil record of the email string: ${civil2.toString()}`); // Converts a given `time:Civil` record to an RFC 5322 formatted string // (e.g `Mon, 3 Dec 2007 10:15:30 GMT`). string emailString = check time:civilToEmailString(civil2, time:PREFER_ZONE_OFFSET); io:println(`Email string of the civil record: ${emailString}`); } ================================================ FILE: examples/time-formatting-and-parsing/time_formatting_and_parsing.md ================================================ # Time formatting/parsing The Ballerina `time` library contains APIs to convert UTC and local time to different string representations (RFC 5322 and RFC 3339) vice versa. For more information on the underlying module, see the [`time` module](https://lib.ballerina.io/ballerina/time/latest/). ::: code time_formatting_and_parsing.bal ::: To run this sample, use the `bal run` command. ::: out time_formatting_and_parsing.out ::: ================================================ FILE: examples/time-formatting-and-parsing/time_formatting_and_parsing.metatags ================================================ description: BBE on how to format and parse UTC and Civil values to different RFC formats. keywords: ballerina, ballerina by examples, bbe, time, utc, rfc3339, rfc5322 ================================================ FILE: examples/time-formatting-and-parsing/time_formatting_and_parsing.out ================================================ $ bal run time_formatting_and_parsing.bal UTC value: [1196676930,0.12] UTC string representation: 2007-12-03T10:15:30.120Z Converted civil value: {"utcOffset":{"hours":5,"minutes":30},"timeAbbrev":"Asia/Colombo","dayOfWeek":1,"year":2021,"month":4,"day":12,"hour":23,"minute":20,"second":50.52} Civil string representation: 2021-04-12T23:20:50.520+05:30 Email formatted string: Mon, 3 Dec 2007 10:15:30 Z Civil record of the email string: {"utcOffset":{"hours":-8,"minutes":0},"timeAbbrev":"America/Los_Angeles","dayOfWeek":3,"year":2021,"month":3,"day":10,"hour":19,"minute":51,"second":55} Email string of the civil record: Wed, 10 Mar 2021 19:51:55 -0800 ================================================ FILE: examples/time-utc/tests/time_utc_test.bal ================================================ import ballerina/io; import ballerina/lang.'string as langstring; import ballerina/test; string[] outputs = []; int counter = 0; // 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(io:Printable... s) { string outstr = ""; foreach io:Printable str in s { outstr = outstr + new PrintableClassImpl(str).toString(); } outputs[counter] = outstr; counter += 1; } // From ballerina/io module to mock io:println() with raw templates class PrintableClassImpl { io:Printable printable; public isolated function init(io:Printable printable) { self.printable = printable; } public isolated function toString() returns string { io:Printable printable = self.printable; if printable is io:PrintableRawTemplate { return new PrintableRawTemplateImpl(printable).toString(); } else if printable is error { return printable.toString(); } else { return printable.toString(); } } } class PrintableRawTemplateImpl { *object:RawTemplate; public io:Printable[] insertions; public isolated function init(io:PrintableRawTemplate printableRawTemplate) { self.strings = printableRawTemplate.strings; self.insertions = printableRawTemplate.insertions; } public isolated function toString() returns string { io:Printable[] templeteInsertions = self.insertions; string[] templeteStrings = self.strings; string templatedString = templeteStrings[0]; foreach int i in 1 ..< (templeteStrings.length()) { io:Printable templateInsert = templeteInsertions[i - 1]; if (templateInsert is io:PrintableRawTemplate) { templatedString += new PrintableRawTemplateImpl(templateInsert).toString() + templeteStrings[i]; } else if (templateInsert is error) { templatedString += templateInsert.toString() + templeteStrings[i]; } else { templatedString += templateInsert.toString() + templeteStrings[i]; } } return templatedString; } } @test:Config {} function testFunc() { test:when(mock_printLn).call("mockPrint"); // Invoking the main function main(); test:assertTrue(langstring:includes(outputs[0], "Number of seconds from the epoch: ")); test:assertTrue(langstring:includes(outputs[1], "Nanoseconds fraction: ")); } ================================================ FILE: examples/time-utc/time_utc.bal ================================================ import ballerina/io; import ballerina/time; public function main() { // Gets the current instant of the system clock (seconds from the epoch of // 1970-01-01T00:00:00). The returned `time:Utc` value represents seconds // from the epoch with nanoseconds precision. // The `time:Utc` is a tuple with `[int, decimal]`. The first member of the // tuple represents the number of seconds from the epoch. The second // member represents the rest of the nanoseconds from the epoch as a // fraction. time:Utc currentUtc = time:utcNow(); io:println(`Number of seconds from the epoch: ${currentUtc[0]}s`); io:println(`Nanoseconds fraction: ${currentUtc[1]}s`); } ================================================ FILE: examples/time-utc/time_utc.md ================================================ # UTC time The Ballerina `time` library contains an API to obtain the current time from the epoch `1970-01-01T00:00:00`. For more information on the underlying module, see the [`time` module](https://lib.ballerina.io/ballerina/time/latest/). ::: code time_utc.bal ::: To run this sample, use the `bal run` command. ::: out time_utc.out ::: ================================================ FILE: examples/time-utc/time_utc.metatags ================================================ description: BBE on how to get the current timestamp in seconds. keywords: ballerina, ballerina by examples, bbe, time, current, epoch, utc ================================================ FILE: examples/time-utc/time_utc.out ================================================ $ bal run time_utc.bal Number of seconds from the epoch: 1621839972s Nanoseconds fraction: 0.005413000s ================================================ FILE: examples/time-utc-and-civil/tests/time_utc_and_civil_test.bal ================================================ import ballerina/io; import ballerina/lang.'string as langstring; import ballerina/test; string[] outputs = []; int counter = 0; // 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(io:Printable... s) { string outstr = ""; foreach io:Printable str in s { outstr = outstr + new PrintableClassImpl(str).toString(); } outputs[counter] = outstr; counter += 1; } // From ballerina/io module to mock io:println() with raw templates class PrintableClassImpl { io:Printable printable; public isolated function init(io:Printable printable) { self.printable = printable; } public isolated function toString() returns string { io:Printable printable = self.printable; if printable is io:PrintableRawTemplate { return new PrintableRawTemplateImpl(printable).toString(); } else if printable is error { return printable.toString(); } else { return printable.toString(); } } } class PrintableRawTemplateImpl { *object:RawTemplate; public io:Printable[] insertions; public isolated function init(io:PrintableRawTemplate printableRawTemplate) { self.strings = printableRawTemplate.strings; self.insertions = printableRawTemplate.insertions; } public isolated function toString() returns string { io:Printable[] templeteInsertions = self.insertions; string[] templeteStrings = self.strings; string templatedString = templeteStrings[0]; foreach int i in 1 ..< (templeteStrings.length()) { io:Printable templateInsert = templeteInsertions[i - 1]; if (templateInsert is io:PrintableRawTemplate) { templatedString += new PrintableRawTemplateImpl(templateInsert).toString() + templeteStrings[i]; } else if (templateInsert is error) { templatedString += templateInsert.toString() + templeteStrings[i]; } else { templatedString += templateInsert.toString() + templeteStrings[i]; } } return templatedString; } } @test:Config {} function testFunc() returns error? { test:when(mock_printLn).call("mockPrint"); // Invoking the main function check main(); test:assertTrue(langstring:includes(outputs[0], "Civil record: {\"timeAbbrev\":\"Z\",")); test:assertEquals(outputs[1], "UTC value of the civil record: [1618269650,0.52]"); } ================================================ FILE: examples/time-utc-and-civil/time_utc_and_civil.bal ================================================ import ballerina/io; import ballerina/time; public function main() returns error? { // Gets the current instant of the system clock (seconds from the epoch of // 1970-01-01T00:00:00). The returned `time:Utc` value represents seconds // from the epoch with nanoseconds precision. time:Utc utc1 = time:utcNow(); // Converts a given `time:Utc` value to a `time:Civil` value. time:Civil civil1 = time:utcToCivil(utc1); io:println(`Civil record: ${civil1.toString()}`); // Converts a given `time:Civil` value to a `time:Utc` value. // Note that, since `time:Civil` is used to represent localized time, // it is mandatory to have the `utcOffset` field to be specified in the // given `time:Civil` value. time:Civil civil2 = { year: 2021, month: 4, day: 13, hour: 4, minute: 50, second: 50.52, timeAbbrev: "Asia/Colombo", utcOffset: {hours: 5, minutes: 30, seconds: 0d} }; time:Utc utc2 = check time:utcFromCivil(civil2); io:println(`UTC value of the civil record: ${utc2.toString()}`); } ================================================ FILE: examples/time-utc-and-civil/time_utc_and_civil.md ================================================ # Time with zone offset The Ballerina `time` library contains APIs to convert UTC to local time and vice versa. For more information on the underlying module, see the [`time` module](https://lib.ballerina.io/ballerina/time/latest/). ::: code time_utc_and_civil.bal ::: To run this sample, use the `bal run` command. ::: out time_utc_and_civil.out ::: ================================================ FILE: examples/time-utc-and-civil/time_utc_and_civil.metatags ================================================ description: BBE on how to convert a given UTC time to a civil record vice versa. keywords: ballerina, ballerina by examples, bbe, time, utc, civil ================================================ FILE: examples/time-utc-and-civil/time_utc_and_civil.out ================================================ $ bal run time_utc_and_civil.bal Civil record: {"timeAbbrev":"Z","dayOfWeek":1,"year":2021,"month":5,"day":24,"hour":7,"minute":7,"second":15.757818} UTC value of the civil record: [1618269650,0.52] ================================================ FILE: examples/time-zone/time_zone.bal ================================================ import ballerina/io; import ballerina/time; public function main() returns error? { // Event recorded in UTC (2022-01-29 22:48:00). time:Utc eventUtcTime = check time:utcFromString("2022-01-29T22:48:00Z"); io:println("Event time in UTC: ", eventUtcTime); // Load the system's default time zone. time:Zone systemZone = check new time:TimeZone(); // Convert UTC event time to the system's local time. time:Civil eventInSystemZone = systemZone.utcToCivil(eventUtcTime); io:println("Event time in system's local time: " + eventInSystemZone.toString()); // Print the event time in 'Europe/London' time zone. printZoneTimeFromUtc(eventUtcTime, "Europe/London"); // Print the event time in 'Asia/Tokyo' time zone. printZoneTimeFromUtc(eventUtcTime, "Asia/Tokyo"); } function printZoneTimeFromUtc(time:Utc utcTime, string zoneId) { time:Zone? zone = time:getZone(zoneId); if zone is time:Zone { time:Civil timeInZone = zone.utcToCivil(utcTime); io:println(string `Event time in ${zoneId} time zone: ${timeInZone.toString()}`); } else { io:println(string `Failed to load the '${zoneId}' time zone.`); } } ================================================ FILE: examples/time-zone/time_zone.md ================================================ # Time Zone The Ballerina `time` library provides APIs for managing and converting time across different time zones. It supports loading system time zone, retrieving specific time zones based on the time zone ID, and enables seamless conversion between UTC and local times across different regions. For more information on the underlying module, see the [`time` module](https://lib.ballerina.io/ballerina/time/latest/). ::: code time_zone.bal ::: To run this sample, use the `bal run` command. ::: out time_zone.out ::: ================================================ FILE: examples/time-zone/time_zone.metatags ================================================ description: BBE on how to use time zone APIs in Ballerina. keywords: ballerina, ballerina by examples, bbe, time, zone, utc, zoneId ================================================ FILE: examples/time-zone/time_zone.out ================================================ $ bal run time_zone.bal Event time in UTC: [1643496480,0] Event time in system's local time: {"timeAbbrev":"Asia/Colombo","dayOfWeek":0,"year":2022,"month":1,"day":30,"hour":4,"minute":18,"second":0} Event time in Europe/London time zone: {"timeAbbrev":"Europe/London","dayOfWeek":6,"year":2022,"month":1,"day":29,"hour":22,"minute":48,"second":0} Event time in Asia/Tokyo time zone: {"timeAbbrev":"Asia/Tokyo","dayOfWeek":0,"year":2022,"month":1,"day":30,"hour":7,"minute":48,"second":0} ================================================ FILE: examples/tracing/tests/tracing_test.bal ================================================ import ballerina/test; import ballerina/http; @test:Config { } function testFunc() { // Invoking the service http:Client httpEndpoint = new ("http://localhost:9234"); string response1 = "Hello, World!"; // Send a GET request to the specified endpoint http:Response|error response = httpEndpoint->get("/hello/sayHello"); if response is http:Response { var res = response.getTextPayload(); if res is error { test:assertFail(msg = "Failed to call the endpoint:"); } else { test:assertEquals(res, response1); } } else { test:assertFail(msg = "Failed to call the endpoint:"); } } ================================================ FILE: examples/tracing/tracing.bal ================================================ import ballerina/http; import ballerina/log; import ballerina/observe; import ballerina/lang.runtime; import ballerinax/jaeger as _; // Simple `Hello` HTTP Service service /hello on new http:Listener(9234) { // Resource functions are invoked with the HTTP caller and the // incoming request as arguments. resource function get sayHello(http:Caller caller, http:Request req) returns error? { http:Response res = new; //Start a child span attaching to the generated system span. int spanId = check observe:startSpan("MyFirstLogicSpan"); //Start a new root span without attaching to the system span. int rootParentSpanId = observe:startRootSpan("MyRootParentSpan"); // Some actual logic will go here, and for example, we have introduced some delay with the sleep. runtime:sleep(1); //Start a new child span for the `MyRootParentSpan` span. int childSpanId = check observe:startSpan("MyRootChildSpan", (), rootParentSpanId); // Some actual logic will go here, and for example, we have introduced some delay with the sleep. runtime:sleep(1); //Finish the `MyRootChildSpan` span. error? result = observe:finishSpan(childSpanId); if (result is error) { log:printError("Error in finishing span", 'error = result); } // Some actual logic will go here, and for example, we have introduced some delay with the sleep. runtime:sleep(1); //Finish the `MyRootParentSpan` span. result = observe:finishSpan(rootParentSpanId); if (result is error) { log:printError("Error in finishing span", 'error = result); } //Some actual logic will go here, and for example, we have introduced some delay with the sleep. runtime:sleep(1); //Finish the created child `MyFirstLogicSpan` span, which was attached to the system trace. result = observe:finishSpan(spanId); if (result is error) { log:printError("Error in finishing span", 'error = result); } //Use a util method to set a string payload. res.setPayload("Hello, World!"); //Send the response back to the caller. result = caller->respond(res); if (result is error) { log:printError("Error sending response", 'error = result); } return (); } } ================================================ FILE: examples/tracing/tracing.client.out ================================================ $ curl http://localhost:9234/hello/sayHello Hello, World! ================================================ FILE: examples/tracing/tracing.md ================================================ # Distributed tracing Ballerina supports Observability out of the box, and Tracing 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 trace the code blocks and measure the time incurred during the actual runtime execution. Also, you can choose to hook the measurement with the default trace created or can create a completely new trace. >**Info:** For more information about configs and observing applications, see [Observe Ballerina programs](/learn/observe-ballerina-programs/). ::: code tracing.bal ::: Invoke the service using cURL and access [Jaeger UI](http://localhost:16686). ::: out tracing.client.out ::: Jaeger is the default tracing tool used in Ballerina. To start the Jaeger, execute the command below. 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 tracing.server.out ::: ================================================ FILE: examples/tracing/tracing.metatags ================================================ description: BBE on how to use the default Tracing Observability feature in Ballerina. keywords: ballerina, ballerina by example, bbe, observability, tracing, opentracing ================================================ FILE: examples/tracing/tracing.server.out ================================================ $ docker run -d -p 13133:13133 -p 16686:16686 -p 55680:55680 jaegertracing/opentelemetry-all-in-one $ BAL_CONFIG_FILES=Config.toml bal run --observability-included tracing.bal ballerina: started publishing traces to Jaeger on localhost:55680 ================================================ FILE: examples/transaction-statement/transaction_statement.bal ================================================ import ballerina/io; public function main() returns error? { // The transaction statement begins a new transaction and executes a block. transaction { doStage1(); doStage2(); // Commit of a transaction must be done explicitly using commit. // A commit must be lexically within a transaction statement and may return an error; check commit; } return; } function doStage1() { io:println("Stage1 completed"); } function doStage2() { io:println("Stage2 completed"); } ================================================ FILE: examples/transaction-statement/transaction_statement.md ================================================ # Transaction statement Ballerina provides support for interacting with a transaction manager. Compile-time guarantees that transactions are bracketed with begin and `commit` or `rollback`. The region in the middle is typed as being a transactional context. Ballerina does not have a transactional memory and includes a transaction manager. The current transaction is a part of the execution context of a strand. ::: code transaction_statement.bal ::: ::: out transaction_statement.out ::: ================================================ FILE: examples/transaction-statement/transaction_statement.metatags ================================================ description: This BBE introduces transaction statement in Ballerina. keywords: ballerina, ballerina by example, bbe, transactions, transaction, commit ================================================ FILE: examples/transaction-statement/transaction_statement.out ================================================ $ bal run transaction_statement.bal Stage1 completed Stage2 completed ================================================ FILE: examples/transactional-named-workers/transactional_named_workers.bal ================================================ import ballerina/io; type Update record { int updateIndex; int stockMnt; }; public function main() returns error? { Update newUpdate = { updateIndex: 132, stockMnt: 3500 }; transaction { check exec(newUpdate); check commit; } } // A `transactional` function can only be called from a `transactional` context transactional function exec(Update u) returns error? { // A `transactional` named worker starts a transaction branch in the current transaction. transactional worker A { bar(); } } transactional function bar() { io:println("bar() invoked"); } ================================================ FILE: examples/transactional-named-workers/transactional_named_workers.md ================================================ # Transactional named workers A named worker within a transactional function can be declared as `transactional`. This will start a new transaction branch for the named worker, as with a distributed transaction. ::: code transactional_named_workers.bal ::: ::: out transactional_named_workers.out ::: ================================================ FILE: examples/transactional-named-workers/transactional_named_workers.metatags ================================================ description: This BBE introduces the transactional named workers in Ballerina. keywords: ballerina, ballerina by example, bbe, workers, transactions, transactional workers, transactional named workers ================================================ FILE: examples/transactional-named-workers/transactional_named_workers.out ================================================ $ bal run transactional_named_workers.bal bar() invoked ================================================ FILE: examples/transactional-qualifier/transactional_qualifier.bal ================================================ import ballerina/io; type Update record { int updateIndex; int stockMnt; }; public function main() returns error? { Update updates = {updateIndex: 0, stockMnt: 100}; transaction { check doUpdate(updates); check commit; } return; } // Called within the transaction statement. transactional function doUpdate(Update u) returns error? { // Calls `foo()` non-transactional function. foo(u); // Calls `bar()` transactional function. bar(u); return; } function foo(Update u) { if transactional { // This is in the transactional context. bar(u); } } transactional function bar(Update u) { io:println("Calling from a transactional context"); } ================================================ FILE: examples/transactional-qualifier/transactional_qualifier.md ================================================ # Transactional qualifier At compile-time, regions of code are typed as being a transactional context. Ballerina guarantees that, whenever that region is executed, there will be a current transaction. A function with a `transactional` qualifier can only be called from a transactional context; function body will be a transactional context. `transactional` is also a boolean expression that tests at runtime whether there is a current transaction: used in a condition results in a transactional context. ::: code transactional_qualifier.bal ::: ::: out transactional_qualifier.out ::: ================================================ FILE: examples/transactional-qualifier/transactional_qualifier.metatags ================================================ description: This BBE introduces the transactional qualifier in Ballerina. keywords: ballerina, ballerina by example, bbe, transactional, transactional qualifier ================================================ FILE: examples/transactional-qualifier/transactional_qualifier.out ================================================ $ bal run transactional_qualifier.bal Calling from a transactional context Calling from a transactional context ================================================ FILE: examples/transform-csv-records-to-custom-types/transform_csv_records_to_custom_types.bal ================================================ import ballerina/data.csv; import ballerina/io; // Represents the details of an employee. type Employee record {| int empId; string empName; string department; decimal salary; |}; // Represents the salary details of an employee. type EmployeeSalary record {| // This annotation is used to map the `empId` field in the source CSV record // to the `id` field in the target record. @csv:Name { value: "empId" } int id; decimal salary; |}; public function main() returns error? { Employee[] employees = [ {empId: 1, empName: "John", department: "Engineering", salary: 1000.0}, {empId: 2, empName: "Doe", department: "HR", salary: 2000.0}, {empId: 3, empName: "Jane", department: "Finance", salary: 3000.0} ]; // Transform the `employees` array into an array of `EmployeeSalary` records. // Only the fields specified in the `EmployeeSalary` type (`id` and `salary`) // are included in the values in the resulting array. EmployeeSalary[] employeeSalaries = check csv:transform(employees); io:println(employeeSalaries); // Transform the `employees` array into an array of `anydata` arrays. anydata[][] employeesArray = check csv:transform(employees); io:println(employeesArray); } ================================================ FILE: examples/transform-csv-records-to-custom-types/transform_csv_records_to_custom_types.md ================================================ # Transform CSV records to custom types Ballerina supports transforming CSV records into various data structures, such as custom records or arrays. It allows users to map CSV records to target types by specifying only the required fields. ::: code transform_csv_records_to_custom_types.bal ::: ::: out transform_csv_records_to_custom_types.out ::: ================================================ FILE: examples/transform-csv-records-to-custom-types/transform_csv_records_to_custom_types.metatags ================================================ description: This BBE demonstrates the transformation of CSV records into Ballerina arrays, both with and without data projection. keywords: ballerina, ballerina by example, bbe, csv, csv records, record, record array, transform, csv data module, data.csv, data projection ================================================ FILE: examples/transform-csv-records-to-custom-types/transform_csv_records_to_custom_types.out ================================================ $ bal run transform_csv_records_to_custom_types.bal [{"id":1,"salary":1000.0},{"id":2,"salary":2000.0},{"id":3,"salary":3000.0}] [[1,"John","Engineering",1000.0],[2,"Doe","HR",2000.0],[3,"Jane","Finance",3000.0]] ================================================ FILE: examples/trap-expression/trap_expression.bal ================================================ import ballerina/io; function hereBeDragons() returns int { // Trigger a panic deep within the call stack. alwaysPanic(); } // Return type `never` indicate that this function will never return normally. I.e., it will always panic. function alwaysPanic() returns never { panic error("deep down in the code"); } public function main() { // Division by 0 triggers a panic. int|error result = trap 1 / 0; if result is error { io:println("Error: ", result); } // Calling the `hereBeDragons` function triggers a panic deep within the call stack, which is trapped here. int|error result2 = trap hereBeDragons(); if result2 is error { io:println("Error: ", result2); } // This will trigger a panic which is not trapped. Thus it will terminate the program. int result3 = hereBeDragons(); io:println("Result: ", result3); } ================================================ FILE: examples/trap-expression/trap_expression.md ================================================ # Trap expression If you have an expression such as a function call that can potentially trigger a panic you can use a `trap` expression to prevent further unwinding of the stack. Then if the evaluation of the expression triggers a panic you will get the `error` value associated with the panic. Otherwise, you will get the result of the expression. ::: code trap_expression.bal ::: ::: out trap_expression.out ::: + [Panics](https://ballerina.io/learn/by-example/panics/) ================================================ FILE: examples/trap-expression/trap_expression.metatags ================================================ description: This BBE demonstrates how the trap expression is used in Ballerina to handle panics keywords: ballerina, ballerina by example, bbe, error, panic, trap ================================================ FILE: examples/trap-expression/trap_expression.out ================================================ $ bal run trap_expression.bal Error: error("{ballerina}DivisionByZero",message=" / by zero") Error: error("deep down in the code") error: deep down in the code at trap_expression:alwaysPanic(trap_expression.bal:10) trap_expression:hereBeDragons(trap_expression.bal:5) trap_expression:main(trap_expression.bal:25) ================================================ FILE: examples/tuples/tuples.bal ================================================ import ballerina/io; public function main() { // Declare a tuple of length 3 where the type of each members are `string`, `int`, `boolean` respectively. [string, int, boolean] person = ["Mike", 24, false]; io:println(person); // Tuple with member type descriptors of same type is equivalent to array with a length. // This is equivalent to `int[3]``. [int, int, int] numbers = [1, 2, 3]; io:println(numbers); // Members of a tuple can be accessed using member access expression. int age = person[1]; io:println(age); // Members of a tuple can be updated using member access expression in LHS of a assignment person[1] = 25; io:println(person); int length = person.length(); // `array:length()` method can be used to get the length of a tuple io:println(length); // Tuples can be used to return multiple values from a function. var personDetails = getPersonDetails(); io:println(personDetails); } function getPersonDetails() returns [int, boolean] { return [30, true]; } ================================================ FILE: examples/tuples/tuples.md ================================================ # Tuples The tuple type is another structured type which creates a list of values like arrays. The main difference between the arrays and the tuples is that an array has only one type applicable to every member of its list. In contrast, in a tuple type, you can individually specify the types for each member. Tuples are most suitable for describing lists with multiple types. Tuples can be used to return multiple values from a function. Tuple type can be declared as a comma separated list of types inside a square bracket `[ ]`. ::: code tuples.bal ::: ::: out tuples.out ::: ## Related links - [Arrays](/learn/by-example/arrays) - [Manipulating an array `(lang.array)`](https://lib.ballerina.io/ballerina/lang.array) - [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/tuples/tuples.metatags ================================================ description: This BBE demonstrates how to create a tuple, create a collection, create list with different member types, returning multiple values from a function, Accessing a tuple, Updating a tuple. keywords: ballerina, ballerina by example, bbe, tuples, collection, size, list, multiple, tuple type, access ================================================ FILE: examples/tuples/tuples.out ================================================ $ bal run tuples.bal ["Mike",24,false] [1,2,3] 24 ["Mike",25,false] 3 [30,true] ================================================ FILE: examples/type-definitions/type_definitions.bal ================================================ import ballerina/io; // Defines a type named `MapArray`. type MapArray map[]; public function main() { // Creates a `MapArray` value. // `arr` has elements which are of `map` type. MapArray arr = [ {"x": "foo"}, {"y": "bar"} ]; io:println(arr[0]); } ================================================ FILE: examples/type-definitions/type_definitions.md ================================================ # Type definitions A type definition gives a name for a type. The name is just an alias for the type. ::: code type_definitions.bal ::: ::: out type_definitions.out ::: ================================================ FILE: examples/type-definitions/type_definitions.metatags ================================================ description: This BBE demonstrates type definitions in Ballerina. keywords: ballerina, ballerina by example, bbe, type definition ================================================ FILE: examples/type-definitions/type_definitions.out ================================================ $ bal run type_definitions.bal {"x":"foo"} ================================================ FILE: examples/type-inclusion-for-records/type_inclusion_for_records.bal ================================================ import ballerina/io; type Person record {| string name; int age; |}; type Student record {| int studentId; string code; |}; // The `PartTimeStudent` record has all the fields of `Person` and `Student`. type PartTimeStudent record {| *Person; *Student; // Overrides the `code` field in `Student`. string:Char code; |}; public function main() { PartTimeStudent student = { name: "Anne", age: 23, studentId: 1001, code: "A" }; io:println(student); } ================================================ FILE: examples/type-inclusion-for-records/type_inclusion_for_records.md ================================================ # Type inclusion for records Type inclusion enables you to create a record by combining fields of other records. You can include the record type `T` in the record type descriptor of another record by using the `*T` notation. This is effectively the same as copying the fields of the included records into the including record. ::: code type_inclusion_for_records.bal ::: ::: out type_inclusion_for_records.out ::: ## Related links - [Records](/learn/by-example/records/) - [Open Records](/learn/by-example/open-records/) - [Default values for record fields](/learn/by-example/default-values-for-record-fields/) ================================================ FILE: examples/type-inclusion-for-records/type_inclusion_for_records.metatags ================================================ description: This BBE demonstrates type inclusion for records, record type inclusion, and including fields of a record. keywords: ballerina, ballerina by example, bbe, record, record type, record field, inclusion, record inclusion ================================================ FILE: examples/type-inclusion-for-records/type_inclusion_for_records.out ================================================ $ bal run type_inclusion_for_records.bal {"code":"A","name":"Anne","age":23,"studentId":1001} ================================================ FILE: examples/type-inference/type_inference.bal ================================================ import ballerina/io; // `var` says that the type of the variable is from the type of expression, which is used to initialize it. var x = "str"; function printLines(string[] sv) { // Type inference with a `foreach` statement. foreach var s in sv { io:println(s); } } public function main() { string[] s = [x, x]; printLines(s); // Infers `x`'s type as `MyClass`. var x = new MyClass(); MyClass _ = x; // Infers the class for `new` as `MyClass`. MyClass _ = new; } class MyClass { function foo() { } } ================================================ FILE: examples/type-inference/type_inference.md ================================================ # Type inference Type inference is local and restricted to a single expression. Overuse of type inference can make the code harder to understand. ::: code type_inference.bal ::: ::: out type_inference.out ::: ================================================ FILE: examples/type-inference/type_inference.metatags ================================================ description: This BBE demonstrates type inference in Ballerina. keywords: ballerina, ballerina by example, bbe, var, ================================================ FILE: examples/type-inference/type_inference.out ================================================ $ bal run type_inference.bal str str ================================================ FILE: examples/typed-binding-pattern/typed_binding_pattern.bal ================================================ import ballerina/io; public function main() returns error? { // The type of variable `name` will be `string` and `age` will be `int`. [string, int] [name, age] = getDetails(); io:println(name); io:println(age); // The type of variable `profession` will be inferred from the expression `Software Engineer`. var profession = "Software Engineer"; io:println(profession); // In the following capture binding pattern, the value `Hello World` gets // matched to the type `string`, resulting in a successful match and // causing the value to be assigned to the variable `greeting`. string greeting = "Hello World"; io:println(greeting); // The inferred type of the following capture binding pattern will be `int|error`. var response = check int:fromString("404"); io:println(response); } function getDetails() returns [string, int] { return ["John", 30]; } ================================================ FILE: examples/typed-binding-pattern/typed_binding_pattern.md ================================================ # Typed binding pattern A typed binding pattern is a combination of a type and a binding pattern. It is used to create the variables occurring in the binding pattern. Use of `var` as the type means that the type is inferred. The two ways in which a typed binding pattern can be used are single use and iterative use. ::: code typed_binding_pattern.bal ::: ::: out typed_binding_pattern.out ::: ## Related links - [Binding patterns](/learn/by-example/binding-patterns/) - [Single use of typed binding patterns](/learn/by-example/single-use-of-typed-binding/) - [Iterative use of typed binding patterns](/learn/by-example/iterative-use-of-typed-binding/) ================================================ FILE: examples/typed-binding-pattern/typed_binding_pattern.metatags ================================================ description: This BBE demostrates the use of a typed binding pattern and a capture binding pattern keywords: ballerina, ballerina by example, bbe, binding pattern, typed binding pattern, capture binding pattern ================================================ FILE: examples/typed-binding-pattern/typed_binding_pattern.out ================================================ $ bal run typed_binding_pattern.bal John 30 Software Engineer Hello World 404 ================================================ FILE: examples/typedesc-type/typedesc_type.bal ================================================ import ballerina/io; type R record { int x; int y; }; // `t` is a `typedesc` representing a record type and type `R`, which is a record is // assigned to it. typedesc t = R; public function main() { R r = {x: 1, y: 2}; any v = r; // The `typeof` operator gets the dynamic type of a value and dynamic types for mutable // structures are inherent types. // It is retrieving the `typedesc` value of `v` and `t`, which are then compared. io:println(typeof v === t); } ================================================ FILE: examples/typedesc-type/typedesc_type.md ================================================ # `typedesc` Type The `typedesc` type is a built-in type that represents a type descriptor and is immutable (a subtype of read-only). It has a type parameter that describes the types that are described by the type descriptors that belong to that type descriptor. A `typedesc` value belongs to the `typedesc` type if the type descriptor describes a type that is a subtype of `T`. ::: code typedesc_type.bal ::: ::: out typedesc_type.out ::: ================================================ FILE: examples/typedesc-type/typedesc_type.metatags ================================================ description: This BBE demonstrates the typedesc type in Ballerina. keywords: ballerina, ballerina by example, bbe, typedesc type ================================================ FILE: examples/typedesc-type/typedesc_type.out ================================================ $ bal run typedesc_type.bal true ================================================ FILE: examples/udp-client/udp_client.bal ================================================ import ballerina/io; import ballerina/udp; public function main() returns error? { // Creates a new connectionless UDP client. // Optionally, you can provide the address that the socket needs to bind // and the timeout in seconds, which specifies the read timeout value. // E.g.: `udp:Client client = new (localHost = "localhost", timeout = 5);` udp:Client socketClient = check new; udp:Datagram datagram = { remoteHost: "localhost", remotePort: 9090, data: "Hello Ballerina echo".toBytes() }; // Sends the data to the remote host. // The parameter is a Datagram record, which contains the `remoteHost`, // `remotePort`, and the `data` to be sent. check socketClient->sendDatagram(datagram); io:println("Datagram was sent to the remote host."); // Waits until the data is received from the remote host. readonly & udp:Datagram result = check socketClient->receiveDatagram(); io:println("Received: ", string:fromBytes(result.data)); // Closes the client and releases the bound port. check socketClient->close(); } ================================================ FILE: examples/udp-client/udp_client.md ================================================ # UDP client - Send/Receive datagram The `udp:Client` sends and receives datagrams. A `udp:Client` is created by optionally giving the address that the socket needs to bind and the timeout in seconds, which specifies the read timeout value. Once connected, `sendDatagram` and `receiveDatagram` synchronous methods are used to send and receive datagrams. Since they are synchronous methods often used in two different strands. Use this to interact with UDP servers or implement low latency connections for time-critical transmissions where data loss is acceptable. ::: code udp_client.bal ::: ## Prerequisites - Run the UDP service given in the [Send/Receive datagram](/learn/by-example/udp-listener/) example. Run the client program by executing the command below. ::: out udp_client.out ::: ## Related links - [`udp:Client` client object - API documentation](https://lib.ballerina.io/ballerina/udp/latest#Client) - [UDP Client - Specification](/spec/udp/#3-client) ================================================ FILE: examples/udp-client/udp_client.metatags ================================================ description: This Ballerina sample demonstrates how to send data to a remote UDP server. keywords: ballerina, ballerina by example, bbe, socket, udp ================================================ FILE: examples/udp-client/udp_client.out ================================================ $ bal run udp_client.bal Datagram was sent to the remote host. Received: Hello Ballerina echo ================================================ FILE: examples/udp-connect-client/udp_connect_client.bal ================================================ // This is the connection oriented client implementation of the UDP socket. import ballerina/io; import ballerina/udp; public function main() returns error? { // Creates a new connection-oriented UDP client by providing the // `remoteHost` and the `remotePort`. // Optionally, you can provide the interface that the socket needs to bind // and the timeout in seconds, which specifies the read timeout value. // E.g.: `udp:ConnectClient socketClient = new ("www.ballerina.com", 80, // localHost = "localhost", timeout = 5);` udp:ConnectClient socketClient = check new ("localhost", 9090); // Sends the data to the connected remote host. // The parameter is a `byte[]`, which contains the data to be sent. check socketClient->writeBytes("Hello Ballerina echo".toBytes()); io:println("Data was sent to the remote host."); // Waits until the data is received from the connected host. readonly & byte[] result = check socketClient->readBytes(); io:println("Received: ", string:fromBytes(result)); // Closes the client and releases the bound port. check socketClient->close(); } ================================================ FILE: examples/udp-connect-client/udp_connect_client.md ================================================ # UDP client - Send/Receive datagram with connection The `udp:ConnectClient` connects to a UDP socket, and then sends and receives datagrams. When connected, data may not be received from or sent to any other address. A `udp:ConnectClient` is created by giving the `remoteHost` and `remotePort`. Once connected, `writeBytes` and `readBytes` synchronous methods are used to send and receive byte streams. Since they are synchronous methods often used in two different strands. The client remains connected until it is explicitly disconnected or until it is closed. Use this to interact with UDP servers or implement low latency connections for time-critical transmissions where data loss is acceptable. ::: code udp_connect_client.bal ::: ## Prerequisites - Run the UDP service given in the [Send/Receive datagram](/learn/by-example/udp-listener/) example. Run the client program by executing the command below. ::: out udp_connect_client.out ::: ## Related links - [`udp:Client` client object - API documentation](https://lib.ballerina.io/ballerina/udp/latest#Client) - [UDP Client - Specification](/spec/udp/#3-client) ================================================ FILE: examples/udp-connect-client/udp_connect_client.metatags ================================================ description: This Ballerina sample demonstrates how to send data to a connected server. keywords: ballerina, ballerina by example, bbe, socket, udp ================================================ FILE: examples/udp-connect-client/udp_connect_client.out ================================================ $ bal run udp_connect_client.bal Data was sent to the remote host. Received: Hello Ballerina echo ================================================ FILE: examples/udp-listener/udp_listener.bal ================================================ import ballerina/io; import ballerina/udp; // Binds the service to the port. // Optionally, you can provide the `remoteHost` and `remotePort` to configure the listener // as a connected listener, which only reads and writes to the configured remote host. // E.g.: `udp:Listener(8080, remoteHost = "www.remote-clinet.com", remotePort = 9090)` service on new udp:Listener(9090) { // This remote method is invoked once the content is received from the // client. You may replace the `onBytes` method with `onDatagram`, which // reads the data as `readonly & udp:Datagram`. remote function onDatagram(readonly & udp:Datagram datagram) returns udp:Datagram { io:println("Received by listener: ", string:fromBytes(datagram.data)); // Echoes back the data to the same client. // This is similar to calling `caller->sendDatagram(datagram);`. return datagram; } } ================================================ FILE: examples/udp-listener/udp_listener.md ================================================ # UDP service - Send/Receive datagram The `udp:Service` allows opening up a UDP socket via a `udp:Listener`. A `udp:Listener` is created by giving the port number, to which `udp:Service` is attached. The listener accepts and serves connections from UDP clients. The `onDatagram` remote method is invoked once the content is received from the client. Use a UDP service to establish connections and communicate over UDP protocol or implement low latency connections for time-critical transmissions where data loss is acceptable. ::: code udp_listener.bal ::: Run the service by executing the command below. ::: out udp_listener.out ::: >**Tip:** You can invoke the above service via the [UDP client](/learn/by-example/udp-client/). ## Related links - [`udp` module - API documentation](https://lib.ballerina.io/ballerina/udp/latest) - [UDP service - Specification](/spec/udp/#4-service) ================================================ FILE: examples/udp-listener/udp_listener.metatags ================================================ description: This Ballerina sample demonstrates read an reply to UDP clients using the UDP Listener. keywords: ballerina, ballerina by example, bbe, socket, udp ================================================ FILE: examples/udp-listener/udp_listener.out ================================================ $ bal run udp_listener.bal Received by listener: Hello Ballerina echo ================================================ FILE: examples/unary-operators/unary_operators.bal ================================================ import ballerina/io; public function main() { int a = 10; // Negate the value of `a`. int negatedInt = -a; io:println(negatedInt); // Invert the bits of `a`. int bitwiseInvertedInt = ~a; io:println(bitwiseInvertedInt); int:Signed8 b = 127; // Negate the value of `b`. int negatedSigned8Int = -b; io:println(negatedSigned8Int); float c = -10.5; // Negate the value of `c`. float negatedFloat = -c; io:println(negatedFloat); // Using the `+` operator returns the value of its operand expression. float unchangedFloat = +c; io:println(unchangedFloat); boolean d = true; // Invert the boolean value of `d`. boolean negatedBoolean = !d; io:println(negatedBoolean); } ================================================ FILE: examples/unary-operators/unary_operators.md ================================================ # Unary operators Ballerina supports unary operators that perform operations on a single operand. The unary `-` operator negates the value of a numeric type by changing its sign. The unary `+` operator returns the value of its numeric operand. The `~` operator inverts the bits of an integer, flipping each 0 to 1 and each 1 to 0. For boolean values, the `!` operator inverts the boolean value, changing `true` to `false` and `false` to `true`. ::: code unary_operators.bal ::: ::: out unary_operators.out ::: ================================================ FILE: examples/unary-operators/unary_operators.metatags ================================================ description: This BBE introduces unary operators in Ballerina. keywords: ballerina, ballerina by example, bbe, operators, unary operators, bitwise operators, int, float, boolean, +, -, ~, ! ================================================ FILE: examples/unary-operators/unary_operators.out ================================================ $ bal run unary_operators.bal -10 -11 -127 10.5 -10.5 false ================================================ FILE: examples/unions/unions.bal ================================================ import ballerina/io; type StructuredName record { string firstName; string lastName; }; // A `Name` type value can be either a `StructuredName` or a `string`. type Name StructuredName|string; public function main() { // `name1` is a `StructuredName`. Name name1 = { firstName: "Rowan", lastName: "Atkinson" }; // `name2` is a `string`. Name name2 = "Leslie Banks"; io:println(nameToString(name1)); io:println(nameToString(name2)); map grades1 = { math: "80", physics: (), chemistry: "76" }; // Parsing a map with grade values that are either the string // representation of an integer or nil results in a map of // just the non-nil grades as integers. map|error parseGrades1 = parseGrades(grades1); io:println(parseGrades1); map grades2 = { math: "80", physics: "N/A", chemistry: "76" }; // Attempting to parse a map with string values that // are not the string representation of a number results // in the function terminating early, returning the error. map|error parseGrades2 = parseGrades(grades2); io:println(parseGrades2); } function nameToString(Name nm) returns string { // Checks whether `nm` belongs to `string` type. if nm is string { // The type of `nm` is narrowed to `string` here. // Therefore, you can directly return `nm` from this // function that specifies `string` as the return type. return nm; } else { // The type of `nm` is narrowed to `StructuredName` here. // Therefore, you can directly access fields defined in // the `StructuredName` record. return nm.firstName + " " + nm.lastName; } } function parseGrades(map grades) returns map|error { map parsedGrades = {}; foreach [string, string|()] [subject, grade] in grades.entries() { // If the `grade` value is `()`, continue on to the next entry. if grade is () { continue; } // The type of `grade` is narrowed to `string` here // since we won't reach here if the value is `()`, due // to the `continue` statement above. // Therefore, we can directly use `grade` where a `string` // value is expected. int|error parsedGrade = int:fromString(grade); // If the `parsedGrade` value is an error value, terminate the // execution of this function and return the error value. if parsedGrade is error { return parsedGrade; } // Since we return the error from the function if `parsedGrade` is `error`, // the type of `parsedGrade` is narrowed to `int` here, // allowing it to be used as an `int`-typed variable when adding // the value to a map of integers. parsedGrades[subject] = parsedGrade; } return parsedGrades; } ================================================ FILE: examples/unions/unions.md ================================================ # Unions `T1|T2` is the union of the sets described by `T1` and `T2`. `T?` is completely equivalent to `T|()`. Unions are untagged. The `is` operator tests whether a value belongs to a specific type. The `is` operator in the condition causes the declared type to be narrowed. ::: code unions.bal ::: ::: out unions.out ::: ================================================ FILE: examples/unions/unions.metatags ================================================ description: This BBE demonstrates unions in Ballerina. keywords: ballerina, ballerina by example, bbe, unions ================================================ FILE: examples/unions/unions.out ================================================ $ bal run unions.bal Rowan Atkinson Leslie Banks {"math":80,"chemistry":76} error("{ballerina/lang.int}NumberParsingError",message="'string' value 'N/A' cannot be converted to 'int'") ================================================ FILE: examples/url-encode-decode/tests/url_encode_decode_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... val) { outputs.push(val.reduce(function (any a, any b) returns string => a.toString() + b.toString(), "").toString()); } @test:Config { } function testFunc() returns error? { test:when(mock_printLn).call("mockPrint"); // Invoking the main function check main(); test:assertEquals(outputs.length(), 2); test:assertTrue(outputs[0].includes("URL encoded value: data%3Dvalue")); test:assertTrue(outputs[1].includes("URL decoded value: data=value")); } ================================================ FILE: examples/url-encode-decode/url_encode_decode.bal ================================================ import ballerina/io; import ballerina/url; public function main() returns error? { string value1 = "data=value"; // Encoding a URL component into a string. string encoded = check url:encode(value1, "UTF-8"); io:println("URL encoded value: ", encoded); string value2 = "data%3Dvalue"; // Decoding an encoded URL component into a string. string decoded = check url:decode(value2, "UTF-8"); io:println("URL decoded value: ", decoded); } ================================================ FILE: examples/url-encode-decode/url_encode_decode.md ================================================ # URL encode/decode operations Ballerina URL API supports encoding/decoding a URL or part of a URL. For more information on the underlying module, see the [`url` module](https://lib.ballerina.io/ballerina/url/latest/). ::: code url_encode_decode.bal ::: To run this sample, place the source code in a file named `url_encode_decode.bal` and use the `bal run` command. ::: out url_encode_decode.out ::: ================================================ FILE: examples/url-encode-decode/url_encode_decode.metatags ================================================ description: BBE on how to perform encoding/decoding a URL or part of a URL. keywords: ballerina, ballerina by example, url encoding, url decoding ================================================ FILE: examples/url-encode-decode/url_encode_decode.out ================================================ $ bal run url_encode_decode.bal URL encoded value: data%3Dvalue URL decoded value: data=value ================================================ FILE: examples/uuid-generation/uuid_generation.bal ================================================ import ballerina/io; import ballerina/uuid; public function main() returns error? { // Generates a UUID of type 1 as a string. string uuid1String = uuid:createType1AsString(); io:println("UUID of type 1 as a string: ", uuid1String); // Generates a UUID of type 1 as a UUID record. uuid:Uuid uuid1Record = check uuid:createType1AsRecord(); io:println("UUID of type 1 as a record: ", uuid1Record); // Generates a UUID of type 3 as a string. string uuid3String = check uuid:createType3AsString( uuid:NAME_SPACE_DNS, "ballerina.io"); io:println("UUID of type 3 as a string: ", uuid3String); // Generates a UUID of type 3 as a record. uuid:Uuid uuid3Record = check uuid:createType3AsRecord( uuid:NAME_SPACE_DNS, "ballerina.io"); io:println("UUID of type 3 as a record: ", uuid3Record); // Generates a UUID of type 4 as a string. string uuid4String = uuid:createType4AsString(); io:println("UUID of type 4 as a string: ", uuid4String); // Generates a UUID of type 4 as a UUID record. uuid:Uuid uuid4Record = check uuid:createType4AsRecord(); io:println("UUID of type 4 as a record: ", uuid4Record); // Generates a UUID of type 5 as a string. string uuid5String = check uuid:createType5AsString( uuid:NAME_SPACE_DNS, "ballerina.io"); io:println("UUID of type 5 as a string: ", uuid5String); // Generates a UUID of type 5 as a record. uuid:Uuid uuid5Record = check uuid:createType5AsRecord( uuid:NAME_SPACE_DNS, "ballerina.io"); io:println("UUID of type 5 as a record: ", uuid5Record); // Generates a nil UUID as a string. string nilUuidString = uuid:nilAsString(); io:println("Nil UUID as a string: ", nilUuidString); // Generates a nil UUID as a UUID record. uuid:Uuid nilUuidRecord = uuid:nilAsRecord(); io:println("Nil UUID as a record: ", nilUuidRecord); } ================================================ FILE: examples/uuid-generation/uuid_generation.md ================================================ # Generate UUID The `uuid` library provides functions related to UUIDs (Universal Unique Identifiers). For more information on the underlying module, see the [`uuid` module](https://lib.ballerina.io/ballerina/uuid/latest/). ::: code uuid_generation.bal ::: To run this sample, use the `bal run` command. ::: out uuid_generation.out ::: ================================================ FILE: examples/uuid-generation/uuid_generation.metatags ================================================ description: BBE on how to to generate different types of UUIDs. keywords: ballerina, ballerina by examples, bbe, uuid, type ================================================ FILE: examples/uuid-generation/uuid_generation.out ================================================ $ bal run uuid_generation.bal UUID of type 1 as a string: 01eb3f05-fbf8-1b92-8711-dc6a5719bb63 UUID of type 1 as a record: {"timeLow":32194310,"timeMid":7997,"timeHiAndVersion":5524,"clockSeqHiAndReserved":170,"clockSeqLo":116,"node":82490221220318} UUID of type 3 as a string: cea5c405-7d11-3fbb-bdfb-9b68497be28b UUID of type 3 as a record: {"timeLow":3466970117,"timeMid":32017,"timeHiAndVersion":16315,"clockSeqHiAndReserved":189,"clockSeqLo":251,"node":170872211759755} UUID of type 4 as a string: 73e0d74e-8a4a-40ce-b1d9-b5b522533852 UUID of type 4 as a record: {"timeLow":2795821625,"timeMid":5327,"timeHiAndVersion":20251,"clockSeqHiAndReserved":161,"clockSeqLo":71,"node":59752348973988} UUID of type 5 as a string: 08aab8bc-c69e-5ea8-8a52-dbb645c67fb5 UUID of type 5 as a record: {"timeLow":145406140,"timeMid":50846,"timeHiAndVersion":24232,"clockSeqHiAndReserved":138,"clockSeqLo":82,"node":241575901167541} Nil UUID as a string: 00000000-0000-0000-0000-000000000000 Nil UUID as a record: {"timeLow":0,"timeMid":0,"timeHiAndVersion":0,"clockSeqHiAndReserved":0,"clockSeqLo":0,"node":0} ================================================ FILE: examples/uuid-operations/uuid_operations.bal ================================================ import ballerina/io; import ballerina/uuid; public function main() returns error? { // Tests a string to see if it is a valid UUID. boolean valid = uuid:validate("4397465e-35f9-11eb-adc1-0242ac120002"); io:println("UUID validated: ", valid.toString()); // Detects the RFC version of a UUID. uuid:Version v = check uuid:getVersion( "4397465e-35f9-11eb-adc1-0242ac120002"); io:println("UUID version: ", v.toString()); // Converts a UUID string to an array of bytes. byte[] uuidBytes1 = check uuid:toBytes( "4397465e-35f9-11eb-adc1-0242ac120002"); io:println("UUID bytes: ", uuidBytes1); // Converts a UUID string to a UUID record. uuid:Uuid uuidRecord1 = check uuid:toRecord( "4397465e-35f9-11eb-adc1-0242ac120002"); io:println("UUID record: ", uuidRecord1); uuid:Uuid uuidRecord = { timeLow: 1133987422, timeMid: 13817, timeHiAndVersion: 4587, clockSeqHiAndReserved: 173, clockSeqLo: 193, node: 2485377957890 }; // Converts a UUID record to a UUID string. string uuidString1 = check uuid:toString(uuidRecord); io:println("UUID string: ", uuidString1); // Converts a UUID record to an array of bytes. byte[] uuidBytes2 = check uuid:toBytes(uuidRecord); io:println("UUID bytes: ", uuidBytes2); // Converts a UUID bytes array to a UUID string. string uuidString2 = check uuid:toString( [67,151,70,94,53,249,17,235,173,193,2,66,172,18,0,2]); io:println("UUID string: ", uuidString2); // Converts a UUID bytes array to a UUID record. uuid:Uuid uuidRecord2 = check uuid:toRecord( [67,151,70,94,53,249,17,235,173,193,2,66,172,18,0,2]); io:println("UUID record: ", uuidRecord2); } ================================================ FILE: examples/uuid-operations/uuid_operations.md ================================================ # UUID operations The `uuid` library provides functions related to UUIDs (Universal Unique Identifiers). For more information on the underlying module, see the [`uuid` module](https://lib.ballerina.io/ballerina/uuid/latest/). ::: code uuid_operations.bal ::: To run this sample use the `bal run` command. ::: out uuid_operations.out ::: ================================================ FILE: examples/uuid-operations/uuid_operations.metatags ================================================ description: BBE on how to to perform operations on UUIDs. keywords: ballerina, ballerina by examples, bbe, uuid, version, validation, string, record, bytes ================================================ FILE: examples/uuid-operations/uuid_operations.out ================================================ $ bal run uuid_operations.bal UUID validated: true UUID version: V1 UUID bytes: [67,151,70,94,53,249,17,235,173,193,2,66,172,18,0,2] UUID record: {"timeLow":1133987422,"timeMid":13817,"timeHiAndVersion":4587,"clockSeqHiAndReserved":173,"clockSeqLo":193,"node":2485377957890} UUID string: 4397465e-35f9-11eb-adc1-0242ac120002 UUID bytes: [67,151,70,94,53,249,17,235,173,193,2,66,172,18,0,2] UUID string: 4397465e-35f9-11eb-adc1-0242ac120002 UUID record: {"timeLow":1133987422,"timeMid":13817,"timeHiAndVersion":4587,"clockSeqHiAndReserved":173,"clockSeqLo":193,"node":2485377957890} ================================================ FILE: examples/variables-and-types/variables_and_types.bal ================================================ import ballerina/io; // Modules and functions can declare variables. You can see both in this example. // Here we declare a variable `greeting` of type `string` and initialize it to `"Hello"`. string greeting = "Hello"; public function main() { // Assignments are statements not expressions. string name = "Ballerina"; io:println(greeting, " ", name); } ================================================ FILE: examples/variables-and-types/variables_and_types.md ================================================ # Variables and types A variable has a type, which constrains what values the variable can hold. There is a built-in set of named types, including `int`, `float`, `boolean`, `string`. ::: code variables_and_types.bal ::: ::: out variables_and_types.out ::: ================================================ FILE: examples/variables-and-types/variables_and_types.metatags ================================================ description: This BBE introduces variables and types in Ballerina. keywords: ballerina, ballerina by example, bbe, variables, types, type system ================================================ FILE: examples/variables-and-types/variables_and_types.out ================================================ $ bal run variables_and_types.bal Hello Ballerina ================================================ FILE: examples/visibility-of-object-fields-and-methods/visibility_of_object_fields_and_methods.bal ================================================ import ballerina/io; class Engineer { // The `public` field is accessible outside the module as well. public string name; // The `private` field is only accessible within the class. private decimal salary; // The `init` method initializes the object. function init(string name) { self.name = name; self.salary = 0; } // The default method is only accessible within the module. function getName() returns string { return self.name; } // The `private` method is only accessible within the class definition. private function addBonus(decimal salary) returns decimal { return salary + 100; } // The `public` method is accessible outside the module as well. public function getSalary() returns decimal { return self.addBonus(self.salary); } public function setSalary(decimal salary) { self.salary = salary; } } public function main() { // Arguments to `new` are passed as arguments to `init`. Engineer engineer = new Engineer("Alice"); engineer.setSalary(1000); io:println(engineer.name); io:println(engineer.getName()); io:println(engineer.getSalary()); } ================================================ FILE: examples/visibility-of-object-fields-and-methods/visibility_of_object_fields_and_methods.md ================================================ # Visibility of object fields and methods The visibility of each field and method of an object in Ballerina can be set to `private`, `public`, or default (without a visibility qualifier). - If the visibility is set to `private`, the field or method is only accessible within the `class` definition. - If the visibility is set to `public`, the field or method is accessible outside the module as well. - If the visibility is set to default (no visibility qualifier), the field or method is only accessible within the module. Attempting to access a private field or method from outside the class definition will result in a compile-time error. ::: code visibility_of_object_fields_and_methods.bal ::: ::: out visibility_of_object_fields_and_methods.out ::: ## Related links - [Object](/learn/by-example/object/) - [Defining classes](/learn/by-example/defining-classes/) - [Object value from class definition](/learn/by-example/object-value-from-class-definition/) ================================================ FILE: examples/visibility-of-object-fields-and-methods/visibility_of_object_fields_and_methods.metatags ================================================ description: This BBE demonstrates an access modifier in the class definition, modifying the visibility of object fields and methods, invoking methods and fields, and defining a class in Ballerina. keywords: ballerina, ballerina by example, bbe, class, object, access modifier, visibility ================================================ FILE: examples/visibility-of-object-fields-and-methods/visibility_of_object_fields_and_methods.out ================================================ $ bal run visibility_of_object_fields_and_methods.bal Alice Alice 1100 ================================================ FILE: examples/waiting-for-workers/waiting_for_workers.bal ================================================ import ballerina/io; public function main() { io:println("Initializing"); worker A { io:println("In worker A"); } io:println("In function worker"); // A worker (function or named) can use `wait` to wait for a named worker. wait A; io:println("After wait A"); } ================================================ FILE: examples/waiting-for-workers/waiting_for_workers.md ================================================ # Waiting for workers Named workers can continue to execute after the function's default worker terminates and the function returns. A worker (function or named) can use `wait` to wait for a named worker. ::: code waiting_for_workers.bal ::: ::: out waiting_for_workers.out ::: ================================================ FILE: examples/waiting-for-workers/waiting_for_workers.metatags ================================================ description: This BBE demonstrates waiting for workers keywords: ballerina, ballerina by example, bbe, worker, wait ================================================ FILE: examples/waiting-for-workers/waiting_for_workers.out ================================================ $ bal run waiting_for_workers.bal Initializing In function worker In worker A After wait A ================================================ FILE: examples/websocket-basic-sample/tests/websocket_basic_sample_test.bal ================================================ import ballerina/test; import ballerina/websocket; string msg = "hey"; @test:Config {} function testText() returns websocket:Error? { websocket:Client wsClient = check new("ws://localhost:9090/chat"); check wsClient->writeMessage(msg); string serviceReply = check wsClient->readMessage(); test:assertEquals(serviceReply, "Hello!, How are you?"); check wsClient->close(); } ================================================ FILE: examples/websocket-basic-sample/websocket_basic_sample.bal ================================================ import ballerina/io; import ballerina/websocket; service /chat on new websocket:Listener(9090) { resource function get .() returns websocket:Service { // Accept the WebSocket upgrade by returning a `websocket:Service`. return new ChatService(); } } service class ChatService { *websocket:Service; // This `remote method` is triggered when a new message is received // from a client. It accepts `anydata` as the function argument. The received data // will be converted to the data type stated as the function argument. remote function onMessage(websocket:Caller caller, string chatMessage) returns error? { io:println(chatMessage); check caller->writeMessage("Hello!, How are you?"); } } ================================================ FILE: examples/websocket-basic-sample/websocket_basic_sample.md ================================================ # WebSocket service - Send/Receive message The `websocket:Service` allows opening up a port via a `websocket:Listener`. A `websocket:Listener` is created by giving the port number, to which `websocket:Service` is attached. The listener accepts and serves connections from WebSocket clients. The `onMessage` remote method receives incoming WebSocket messages. There are a few other remote methods to receive other types of WebSocket messages. The `onOpen` remote method is dispatched as soon as the WebSocket handshake is completed and the connection is established, `onPing` and `onPong` remote methods are dispatched upon receiving ping and pong messages respectively, `onIdleTimeout` remote method is dispatched when the idle timeout is reached, `onClose` is dispatched when a close frame with a `statusCode` and a `reason` is received and finally the `onError` is dispatched when an error occurs in the WebSocket connection. Use this service to implement user applications where you need to establish two-way communication over the WebSocket protocol. ::: code websocket_basic_sample.bal ::: Run the service by executing the command below. ::: out websocket_basic_sample.out ::: >**Tip:** You can invoke the above service via the [WebSocket client](/learn/by-example/websocket-client/). ## Related links - [`websocket` module - API documentation](https://lib.ballerina.io/ballerina/websocket/latest) - [WebSocket service - Specification](/spec/websocket/#3-service-types) ================================================ FILE: examples/websocket-basic-sample/websocket_basic_sample.metatags ================================================ description: This BBE demonstrates the basic functions of a WebSocket server. keywords: ballerina, ballerina by example, bbe, websocket, basic sample, server ================================================ FILE: examples/websocket-basic-sample/websocket_basic_sample.out ================================================ $ bal run websocket_basic_sample.bal ================================================ FILE: examples/websocket-binary-client/websocket_binary_client.md ================================================ # Handle binary messages with client The WebSocket client can be used to connect to and interact with a WebSocket server in a Synchronous manner.This example demonstrates how to read and write binary messages using ballerina websocket client. For more information on the underlying module, see the [`websocket` module](https://lib.ballerina.io/ballerina/websocket/latest/). ::: code ./examples/websocket-binary-client/websocket_binary_client.bal ::: ::: out ./examples/websocket-binary-client/websocket_binary_client.out ::: ================================================ FILE: examples/websocket-client/websocket_client.bal ================================================ import ballerina/io; import ballerina/websocket; public function main() returns error? { // Create a new WebSocket client. websocket:Client chatClient = check new ("ws://localhost:9090/chat"); // Write a message to the server using `writeMessage`. // This function accepts `anydata`. If the given type is a `byte[]`, the message will be sent as // binary frames and the rest of the data types will be sent as text frames. check chatClient->writeMessage("Hello John!"); // Read a message sent from the server using `readMessage`. // The contextually-expected data type is inferred from the LHS variable type. The received data // will be converted to that particular data type. string message = check chatClient->readMessage(); io:println(message); } ================================================ FILE: examples/websocket-client/websocket_client.md ================================================ # WebSocket client - Send/Receive message The `websocket:Client` connects to a given WebSocket server, and then sends and receives WebSocket frames. A `websocket:Client` is created by giving the URL of the server. Once connected, `writeMessage` and `readMessage` synchronous methods are used to send and receive messages. Since they are synchronous methods often used in two different strands. Use this to interact with WebSocket servers or implement user applications based on WebSocket. ::: code websocket_client.bal ::: ## Prerequisites - Run the WebSocket service given in the [Send/Receive message](/learn/by-example/websocket-basic-sample/) example. Run the client program by executing the command below. ::: out websocket_client.out ::: ## Related links - [`websocket:Client` client object - API documentation](https://lib.ballerina.io/ballerina/websocket/latest#Client) - [WebSocket Client - Specification](/spec/websocket/#4-client) ================================================ FILE: examples/websocket-client/websocket_client.metatags ================================================ description: This BBE demonstrates how a WebSocket client can be used to connect to and interact with a WebSocket server using Ballerina. keywords: ballerina, ballerina by example, bbe, websocket, sync, client ================================================ FILE: examples/websocket-client/websocket_client.out ================================================ $ bal run websocket_text_client.bal Hello!, How are you? ================================================ FILE: examples/websocket-client-basic-auth/websocket_client_basic_auth.bal ================================================ import ballerina/io; import ballerina/websocket; public function main() returns error? { // Defines the WebSocket client to call the Basic authentication secured APIs. // The client is enriched with the `Authorization: Basic ` header by // passing the `websocket:CredentialsConfig` for the `auth` configuration of the client. websocket:Client chatClient = check new ("wss://localhost:9090/chat", auth = { username: "ldclakmal", password: "ldclakmal@123" }, secureSocket = { cert: "../resource/path/to/public.crt" } ); check chatClient->writeMessage("Hello, John!"); string chatMessage = check chatClient->readMessage(); io:println(chatMessage); } ================================================ FILE: examples/websocket-client-basic-auth/websocket_client_basic_auth.md ================================================ # WebSocket client - Basic authentication The `websocket:Client` can connect to a service that is secured with basic authentication by adding the `Authorization: Basic ` header to the initial HTTP request. The username and password for basic authentication can be specified in the `auth` field of the client configuration. ::: code websocket_client_basic_auth.bal ::: ## Prerequisites - Run the WebSocket service given in the [Basic authentication file user store](/learn/by-example/websocket-service-basic-auth-file-user-store/) example. Run the client program by executing the command below. ::: out websocket_client_basic_auth.out ::: ## Related Links - [`websocket` module - API documentation](https://lib.ballerina.io/ballerina/websocket/latest) - [`auth` module - API documentation](https://lib.ballerina.io/ballerina/auth/latest/) - [WebSocket authentication - Specification](/spec/websocket/#52-authentication-and-authorization) ================================================ FILE: examples/websocket-client-basic-auth/websocket_client_basic_auth.metatags ================================================ description: BBE on how to secure WebSocket client with Basic Auth in Ballerina. keywords: ballerina, ballerina by example, bbe, websocket, auth, basic auth ================================================ FILE: examples/websocket-client-basic-auth/websocket_client_basic_auth.out ================================================ $ bal run websocket_client_basic_auth.bal Hello, How are you? ================================================ FILE: examples/websocket-client-bearer-token-auth/websocket_client_bearer_token_auth.bal ================================================ import ballerina/io; import ballerina/websocket; public function main() returns error? { // Defines the WebSocket client to call the secured APIs. // The client is enriched with the `Authorization: Bearer ` header by // passing the `websocket:BearerTokenConfig` for the `auth` configuration of the client. websocket:Client chatClient = check new ("wss://localhost:9090/chat", auth = { token: "56ede317-4511-44b4-8579-a08f094ee8c5" }, secureSocket = { cert: "../resource/path/to/public.crt" } ); check chatClient->writeMessage("Hello, John!"); string chatMessage = check chatClient->readMessage(); io:println(chatMessage); } ================================================ FILE: examples/websocket-client-bearer-token-auth/websocket_client_bearer_token_auth.md ================================================ # WebSocket client - Bearer token authentication The `websocket:Client` can connect to a service that is secured with bearer token authentication by adding the `Authorization: Bearer ` header to the initial HTTP request. The bearer token can be specified in the `auth` field of the client configuration. ::: code websocket_client_bearer_token_auth.bal ::: Run the client program by executing the command below. ::: out websocket_client_bearer_token_auth.out ::: ## Related Links - [`websocket` module - API documentation](https://lib.ballerina.io/ballerina/websocket/latest) - [WebSocket authentication - Specification](/spec/websocket/#52-authentication-and-authorization) ================================================ FILE: examples/websocket-client-bearer-token-auth/websocket_client_bearer_token_auth.metatags ================================================ description: BBE on how to secure WebSocket client with Bearer token auth in Ballerina. keywords: ballerina, ballerina by example, bbe, websocket, auth, jwt auth ================================================ FILE: examples/websocket-client-bearer-token-auth/websocket_client_bearer_token_auth.out ================================================ $ bal run websocket_client_bearer_token_auth.bal Hello, How are you? ================================================ FILE: examples/websocket-client-mutual-ssl/websocket_client_mutual_ssl.bal ================================================ import ballerina/io; import ballerina/websocket; public function main() returns error? { // A WebSocket client can be configured to initiate new connections that are // secured via mutual SSL. // The `websocket:ClientSecureSocket` record provides the SSL-related configurations. websocket:Client chatClient = check new ("wss://localhost:9090/chat", secureSocket = { key: { certFile: "../resource/path/to/public.crt", keyFile: "../resource/path/to/private.key" }, cert: "../resource/path/to/public.crt" } ); check chatClient->writeMessage("Hello, John!"); string chatMessage = check chatClient->readMessage(); io:println(chatMessage); } ================================================ FILE: examples/websocket-client-mutual-ssl/websocket_client_mutual_ssl.md ================================================ # WebSocket client - Mutual SSL The `websocket:Client` allows you to open up a connection secured with mutual SSL (mTLS), which is a certificate-based authentication process in which two parties (the client and server) authenticate each other by verifying the digital certificates. It ensures that both parties are assured of each other's identity. A `websocket:Client` secured with mutual SSL is created by providing the `secureSocket` configurations which require the client's public certificate as the `certFile`, the client's private key as the `keyFile`, and the server's certificate as the `cert`. Use this to interact with mTLS-encrypted WebSocket servers. ::: code websocket_client_mutual_ssl.bal ::: ## Prerequisites - Run the WebSocket service given in the [Mutual SSL](/learn/by-example/websocket-service-mutual-ssl/) example. Run the client program by executing the command below. ::: out websocket_client_mutual_ssl.out ::: ## Related Links - [`websocket` module - API documentation](https://lib.ballerina.io/ballerina/websocket/latest) - [WebSocket SSL/TLS - Specification](/spec/websocket/#5-securing-the-websocket-connections) ================================================ FILE: examples/websocket-client-mutual-ssl/websocket_client_mutual_ssl.metatags ================================================ description: BBE on how to secure WebSocket client with mutual SSL. keywords: ballerina, ballerina by example, bbe, websocket, mutual ssl, ssl protocols, ciphers ================================================ FILE: examples/websocket-client-mutual-ssl/websocket_client_mutual_ssl.out ================================================ $ bal run websocket_client_mutual_ssl.bal Hello, How are you? ================================================ FILE: examples/websocket-client-oauth2-client-cred-grant-type/websocket_client_oauth2_client_cred_grant_type.bal ================================================ import ballerina/io; import ballerina/websocket; public function main() returns error? { // Defines the WebSocket client to call the OAuth2 secured APIs. // The client is enriched with the `Authorization: Bearer ` header by // passing the `websocket:OAuth2ClientCredentialsGrantConfig` for the `auth` configuration of the client. websocket:Client chatClient = check new ("wss://localhost:9090/chat", auth = { tokenUrl: "https://localhost:9445/oauth2/token", clientId: "FlfJYKBD2c925h4lkycqNZlC2l4a", clientSecret: "PJz0UhTJMrHOo68QQNpvnqAY_3Aa", scopes: "admin", clientConfig: { secureSocket: { cert: "../resource/path/to/public.crt" } } }, secureSocket = { cert: "../resource/path/to/public.crt" } ); check chatClient->writeMessage("Hello, John!"); string chatMessage = check chatClient->readMessage(); io:println(chatMessage); } ================================================ FILE: examples/websocket-client-oauth2-client-cred-grant-type/websocket_client_oauth2_client_cred_grant_type.md ================================================ # WebSocket client - OAuth2 client credentials grant type The `websocket:Client` can connect to a service that is secured with the OAuth2 client credentials grant type by adding the `Authorization: Bearer ` header to the initial HTTP request. The required configurations for this grant type can be specified in the `auth` field of the client configuration. ::: code websocket_client_oauth2_client_cred_grant_type.bal ::: ## Prerequisites - Run the WebSocket service given in the [OAuth2](/learn/by-example/websocket-service-oauth2/) example. Run the client program by executing the command below. ::: out websocket_client_oauth2_client_cred_grant_type.out ::: ## Related Links - [`websocket` module - API documentation](https://lib.ballerina.io/ballerina/websocket/latest) - [`oauth2` module - API documentation](https://lib.ballerina.io/ballerina/oauth2/latest/) - [WebSocket authentication - Specification](/spec/websocket/#52-authentication-and-authorization) ================================================ FILE: examples/websocket-client-oauth2-client-cred-grant-type/websocket_client_oauth2_client_cred_grant_type.metatags ================================================ description: BBE on how to secure WebSocket client with OAuth2 client credentials grant type in Ballerina. keywords: ballerina, ballerina by example, bbe, websocket, auth, oauth2, client credentials grant type ================================================ FILE: examples/websocket-client-oauth2-client-cred-grant-type/websocket_client_oauth2_client_cred_grant_type.out ================================================ $ bal run websocket_client_oauth2_client_credentials_grant_type.bal Hello, How are you? ================================================ FILE: examples/websocket-client-oauth2-jwt-bearer-grant-type/websocket_client_oauth2_jwt_bearer_grant_type.bal ================================================ import ballerina/io; import ballerina/websocket; public function main() returns error? { // Defines the WebSocket client to call the OAuth2 secured APIs. // The client is enriched with the `Authorization: Bearer ` header by // passing the `websocket:OAuth2JwtBearerGrantConfig` for the `auth` configuration of the client. websocket:Client chatClient = check new ("wss://localhost:9090/foo/bar", auth = { tokenUrl: "https://localhost:9445/oauth2/token", assertion: "eyJhbGciOiJFUzI1NiIsImtpZCI6Ij[...omitted for brevity...]", clientId: "FlfJYKBD2c925h4lkycqNZlC2l4a", clientSecret: "PJz0UhTJMrHOo68QQNpvnqAY_3Aa", scopes: "admin", clientConfig: { secureSocket: { cert: "../resource/path/to/public.crt" } } }, secureSocket = { cert: "../resource/path/to/public.crt" } ); check chatClient->writeMessage("Hello, John!"); string chatMessage = check chatClient->readMessage(); io:println(chatMessage); } ================================================ FILE: examples/websocket-client-oauth2-jwt-bearer-grant-type/websocket_client_oauth2_jwt_bearer_grant_type.md ================================================ # WebSocket client - OAuth2 JWT bearer grant type The `websocket:Client` can connect to a service that is secured with the OAuth2 JWT bearer grant type by adding the `Authorization: Bearer ` header to the initial HTTP request. The required configurations for this grant type can be specified in the `auth` field of the client configuration. ::: code websocket_client_oauth2_jwt_bearer_grant_type.bal ::: ## Prerequisites - Run the WebSocket service given in the [OAuth2](/learn/by-example/websocket-service-oauth2/) example. Run the client program by executing the command below. ::: out websocket_client_oauth2_jwt_bearer_grant_type.out ::: ## Related Links - [`websocket` module - API documentation](https://lib.ballerina.io/ballerina/websocket/latest) - [`oauth2` module - API documentation](https://lib.ballerina.io/ballerina/oauth2/latest/) - [WebSocket authentication - Specification](/spec/websocket/#52-authentication-and-authorization) ================================================ FILE: examples/websocket-client-oauth2-jwt-bearer-grant-type/websocket_client_oauth2_jwt_bearer_grant_type.metatags ================================================ description: BBE on how to secure WebSocket client with OAuth2 JWT bearer grant type in Ballerina. keywords: ballerina, ballerina by example, bbe, websocket, auth, oauth2, jwt bearer grant type ================================================ FILE: examples/websocket-client-oauth2-jwt-bearer-grant-type/websocket_client_oauth2_jwt_bearer_grant_type.out ================================================ $ bal run websocket_client_oauth2_jwt_bearer_grant_type.bal Hello, How are you? ================================================ FILE: examples/websocket-client-oauth2-password-grant-type/websocket_client_oauth2_password_grant_type.bal ================================================ import ballerina/io; import ballerina/websocket; import ballerina/oauth2; public function main() returns error? { // Defines the WebSocket client to call the OAuth2 secured APIs. // The client is enriched with the `Authorization: Bearer ` header by // passing the `websocket:OAuth2PasswordGrantConfig` to the `auth` configuration of the client. websocket:Client chatClient = check new ("wss://localhost:9090/chat", auth = { tokenUrl: "https://localhost:9445/oauth2/token", username: "admin", password: "admin", clientId: "FlfJYKBD2c925h4lkycqNZlC2l4a", clientSecret: "PJz0UhTJMrHOo68QQNpvnqAY_3Aa", scopes: "admin", refreshConfig: oauth2:INFER_REFRESH_CONFIG, clientConfig: { secureSocket: { cert: "../resource/path/to/public.crt" } } }, secureSocket = { cert: "../resource/path/to/public.crt" } ); check chatClient->writeMessage("Hello, John!"); string chatMessage = check chatClient->readMessage(); io:println(chatMessage); } ================================================ FILE: examples/websocket-client-oauth2-password-grant-type/websocket_client_oauth2_password_grant_type.md ================================================ # WebSocket client - OAuth2 password grant type The `websocket:Client` can connect to a service that is secured with the OAuth2 password grant type by adding the `Authorization: Bearer ` header to the initial HTTP request. The required configurations for this grant type can be specified in the `auth` field of the client configuration. Use this grant type when you need to exchange the user's credentials for an access token. ::: code websocket_client_oauth2_password_grant_type.bal ::: ## Prerequisites - Run the WebSocket service given in the [OAuth2](/learn/by-example/websocket-service-oauth2/) example. Run the client program by executing the command below. ::: out websocket_client_oauth2_password_grant_type.out ::: ## Related Links - [`websocket` module - API documentation](https://lib.ballerina.io/ballerina/websocket/latest) - [`oauth2` module - API documentation](https://lib.ballerina.io/ballerina/oauth2/latest/) - [WebSocket authentication - Specification](/spec/websocket/#52-authentication-and-authorization) ================================================ FILE: examples/websocket-client-oauth2-password-grant-type/websocket_client_oauth2_password_grant_type.metatags ================================================ description: BBE on how to secure WebSocket client with OAuth2 password grant type in Ballerina. keywords: ballerina, ballerina by example, bbe, websocket, auth, oauth2, password grant type ================================================ FILE: examples/websocket-client-oauth2-password-grant-type/websocket_client_oauth2_password_grant_type.out ================================================ $ bal run websocket_client_oauth2_password_grant_type.bal Hello, How are you? ================================================ FILE: examples/websocket-client-oauth2-refresh-token-grant-type/websocket_client_oauth2_refresh_token_grant_type.bal ================================================ import ballerina/io; import ballerina/websocket; public function main() returns error? { // Defines the WebSocket client to call the OAuth2 secured APIs. // The client is enriched with the `Authorization: Bearer ` header by // passing the `websocket:OAuth2RefreshTokenGrantConfig` for the `auth` configuration of the client. websocket:Client chatClient = check new ("wss://localhost:9090/chat", auth = { refreshUrl: "https://localhost:9445/oauth2/token", refreshToken: "24f19603-8565-4b5f-a036-88a945e1f272", clientId: "FlfJYKBD2c925h4lkycqNZlC2l4a", clientSecret: "PJz0UhTJMrHOo68QQNpvnqAY_3Aa", scopes: "admin", clientConfig: { secureSocket: { cert: "../resource/path/to/public.crt" } } }, secureSocket = { cert: "../resource/path/to/public.crt" } ); check chatClient->writeMessage("Hello, John!"); string chatMessage = check chatClient->readMessage(); io:println(chatMessage); } ================================================ FILE: examples/websocket-client-oauth2-refresh-token-grant-type/websocket_client_oauth2_refresh_token_grant_type.md ================================================ # WebSocket client - OAuth2 refresh token grant type The `websocket:Client` can connect to a service that is secured with the OAuth2 refresh token grant type by adding the `Authorization: Bearer ` header to the initial HTTP request. The required configurations for this grant type can be specified in the `auth` field of the client configuration. Use this to retrieve an access token automatically when it is expired. ::: code websocket_client_oauth2_refresh_token_grant_type.bal ::: ## Prerequisites - Run the WebSocket service given in the [OAuth2](/learn/by-example/websocket-service-oauth2/) example. Run the client program by executing the command below. ::: out websocket_client_oauth2_refresh_token_grant_type.out ::: ## Related Links - [`websocket` module - API documentation](https://lib.ballerina.io/ballerina/websocket/latest) - [`oauth2` module - API documentation](https://lib.ballerina.io/ballerina/oauth2/latest/) - [WebSocket authentication - Specification](/spec/websocket/#52-authentication-and-authorization) ================================================ FILE: examples/websocket-client-oauth2-refresh-token-grant-type/websocket_client_oauth2_refresh_token_grant_type.metatags ================================================ description: BBE on how to secure WebSocket client with OAuth2 refresh token grant type in Ballerina. keywords: ballerina, ballerina by example, bbe, websocket, auth, oauth2, refresh token grant type ================================================ FILE: examples/websocket-client-oauth2-refresh-token-grant-type/websocket_client_oauth2_refresh_token_grant_type.out ================================================ $ bal run websocket_client_oauth2_refresh_token_grant_type.bal Hello, How are you? ================================================ FILE: examples/websocket-client-payload-constraint-validation/websocket_client_payload_constraint_validation.bal ================================================ import ballerina/constraint; import ballerina/io; import ballerina/websocket; // Add a constraint for the maximum length. @constraint:String { maxLength: 20 } public type Chat string; public function main() returns error? { websocket:Client chatClient = check new ("ws://localhost:9090/chat"); check chatClient->writeMessage("Hello John!"); // Run `readMessage()` in a loop in a separate strand to continuously read messages. Chat message = check chatClient->readMessage(); io:println(message); } ================================================ FILE: examples/websocket-client-payload-constraint-validation/websocket_client_payload_constraint_validation.md ================================================ # WebSocket client - Payload constraint validation The Ballerina constraint package allows you to add additional constraints to the received payload. Through client payload constraint validation, the payload can be validated according to the defined constraints. The constraint validation happens along with the data binding step in the `readMessage` remote method. Constraints can be added to a given data type using different annotations. If the validation fails, a `PayloadBindingError` will be returned with the validation details. Use this to validate the receiving payload, which allows you to guard against unnecessary processing and malicious payloads. ::: code websocket_client_payload_constraint_validation.bal ::: ## Prerequisites - Run the WebSocket service given in the [Send/Receive message](/learn/by-example/websocket-basic-sample/) example. Run the client program by executing the command below. ::: out websocket_client_payload_constraint_validation.out ::: ## Related links - [`websocket:Client` client object - API documentation](https://lib.ballerina.io/ballerina/websocket/latest#Client) - [WebSocket Client - Specification](/spec/websocket/#4-client) - [Constraint BBE](/learn/by-example/constraint-validations/) ================================================ FILE: examples/websocket-client-payload-constraint-validation/websocket_client_payload_constraint_validation.metatags ================================================ description: BBE demonstrates the constraint validation capability of the WebSocket client. keywords: ballerina, ballerina by example, bbe, websocket, data binding, client, constraint, validation ================================================ FILE: examples/websocket-client-payload-constraint-validation/websocket_client_payload_constraint_validation.out ================================================ $ bal run websocket_client_payload_constraint_validation.bal Hello!, How are you? ================================================ FILE: examples/websocket-client-self-signed-jwt-auth/websocket_client_self_signed_jwt_auth.bal ================================================ import ballerina/io; import ballerina/websocket; public function main() returns error? { // Defines the WebSocket client to call the JWT authentication secured APIs. // The client is enriched with the `Authorization: Bearer ` header by // passing the `websocket:JwtIssuerConfig` for the `auth` configuration of the // client. A self-signed JWT is issued before the request is sent. websocket:Client chatClient = check new ("wss://localhost:9090/chat", auth = { username: "ballerina", issuer: "wso2", audience: ["ballerina", "ballerina.org", "ballerina.io"], keyId: "5a0b754-895f-4279-8843-b745e11a57e9", jwtId: "JlbmMiOiJBMTI4Q0JDLUhTMjU2In", customClaims: { "scp": "admin" }, expTime: 3600, signatureConfig: { config: { keyFile: "../resource/path/to/private.key" } } }, secureSocket = { cert: "../resource/path/to/public.crt" } ); check chatClient->writeMessage("Hello, John!"); string chatMessage = check chatClient->readMessage(); io:println(chatMessage); } ================================================ FILE: examples/websocket-client-self-signed-jwt-auth/websocket_client_self_signed_jwt_auth.md ================================================ # WebSocket client - Self signed JWT authentication The `websocket:Client` can connect to a service that is secured with self-signed JWT by adding the `Authorization: Bearer ` header by passing the `websocket:JwtIssuerConfig` to the `auth` configuration of the client. A self-signed JWT is issued before the request is sent. ::: code websocket_client_self_signed_jwt_auth.bal ::: ## Prerequisites - Run the WebSocket service given in the [JWT authentication](/learn/by-example/websocket-service-jwt-auth/) example. Run the client program by executing the command below. ::: out websocket_client_self_signed_jwt_auth.out ::: ## Related Links - [`websocket` module - API documentation](https://lib.ballerina.io/ballerina/websocket/latest) - [`oauth2` module - API documentation](https://lib.ballerina.io/ballerina/oauth2/latest/) - [WebSocket authentication - Specification](/spec/websocket/#52-authentication-and-authorization) ================================================ FILE: examples/websocket-client-self-signed-jwt-auth/websocket_client_self_signed_jwt_auth.metatags ================================================ description: BBE on how to secure WebSocket client with self-signed JWT Auth in Ballerina. keywords: ballerina, ballerina by example, bbe, websocket, auth, jwt auth ================================================ FILE: examples/websocket-client-self-signed-jwt-auth/websocket_client_self_signed_jwt_auth.out ================================================ $ bal run websocket_client_self_signed_jwt_auth.bal Hello, How are you? ================================================ FILE: examples/websocket-client-ssl-tls/websocket_client_ssl_tls.bal ================================================ import ballerina/websocket; import ballerina/io; public function main() returns error? { // A WebSocket client can be configured to communicate through WSS as well. // To secure a client using TLS/SSL, the client needs to be configured with // a certificate file of the listener. // The `websocket:ClientSecureSocket` record provides the SSL-related configurations of the client. websocket:Client chatClient = check new ("wss://localhost:9090/chat", secureSocket = { cert: "../resource/path/to/public.crt" } ); check chatClient->writeMessage("Hello, John!"); string chatMessage = check chatClient->readMessage(); io:println(chatMessage); } ================================================ FILE: examples/websocket-client-ssl-tls/websocket_client_ssl_tls.md ================================================ # WebSocket client - SSL/TLS The `websocket:Client` secured with SSL/TLS connects to a given SSL/TLS-secured WebSocket server (WSS). A `websocket:Client` secured with SSL/TLS is created by providing the `secureSocket` configurations which require the server's public certificate as the `cert`. Use this to interact with TLS-encrypted WebSocket servers. ::: code websocket_client_ssl_tls.bal ::: ## Prerequisites - Run the WebSocket service given in the [SSL/TLS](/learn/by-example/websocket-service-ssl-tls/) example. Run the client program by executing the command below. ::: out websocket_client_ssl_tls.out ::: ## Related Links - [`websocket` module - API documentation](https://lib.ballerina.io/ballerina/websocket/latest) - [WebSocket SSL/TLS - Specification](/spec/websocket/#5-securing-the-websocket-connections) ================================================ FILE: examples/websocket-client-ssl-tls/websocket_client_ssl_tls.metatags ================================================ description: BBE on how to secure WebSocket client with SSL. keywords: ballerina, ballerina by example, bbe, websocket, wss, ssl, tls ================================================ FILE: examples/websocket-client-ssl-tls/websocket_client_ssl_tls.out ================================================ $ bal run http_client_ssl_tls.bal Hello, How are you? ================================================ FILE: examples/websocket-query-parameter/websocket_query_parameter.bal ================================================ import ballerina/websocket; service /chat on new websocket:Listener(9090) { // The `userName` parameter in the resource method is treated as a query parameter, // which is extracted from the request URI. For example, invoking this service with // the URI `ws://localhost:9090/chat?userName=John` passes `John` as the value // to the `userName` parameter defined in the resource method. resource function get .(string userName) returns websocket:Service { return new ChatService(userName); } } service class ChatService { *websocket:Service; private final string userName; function init(string userName) { self.userName = userName; } remote function onOpen(websocket:Caller caller) returns error? { check caller->writeMessage(string `Welcome ${self.userName}!`); } } ================================================ FILE: examples/websocket-query-parameter/websocket_query_parameter.md ================================================ # WebSocket service - Query parameter The parameter in the `get` resource method represents the query segment of the request URL. The parameter name should match the query key, and its value is mapped at runtime by extracting it from the URL. Supported types for query parameters include `string`, `int`, `float`, `boolean`, and `decimal`. Query parameter types can be nilable (e.g., `string? bar`). When a request contains query segments, retrieving them as resource arguments is simpler and highly recommended. Alternatively, the `http:Request` object can be used as the parameter of the `get` resource method, which also exposes methods to retrieve query parameters as well. ::: code websocket_query_parameter.bal ::: Run the service as follows. ::: out websocket_query_parameter.server.out ::: >**Tip:** You can invoke the above service via the [WebSocket client](/learn/by-example/websocket-client/). ## Related links - [`websocket` module - API documentation](https://lib.ballerina.io/ballerina/websocket/latest) - [WebSocket service - Specification](/spec/websocket/#31-upgrade-service) ================================================ FILE: examples/websocket-query-parameter/websocket_query_parameter.metatags ================================================ description: This example is on how to extract the query parameters from websocket URL. keywords: ballerina, ballerina by example, bbe, websocket service, query parameter ================================================ FILE: examples/websocket-query-parameter/websocket_query_parameter.server.out ================================================ $ bal run websocket_query_parameter.bal ================================================ FILE: examples/websocket-retry-client/websocket_retry_client.bal ================================================ import ballerina/websocket; public function main() returns error? { websocket:Client chatRetryClient = check new ("ws://localhost:9090/chat", { // Set the maximum retry count to 5 so that it will try 5 times with the interval of // 5 second in between the retry attempts. retryConfig: { maxCount: 5, interval: 5 } }); check chatRetryClient->writeMessage("Hey Sam!"); } ================================================ FILE: examples/websocket-retry-client/websocket_retry_client.md ================================================ # WebSocket client - Retry The `websocket:Client` with enabled `retry` automatically tries to reconnect to the given backend. The client only retries when there is a connection error at the handshake phase or if an abnormal closure with the status code(1006) is received once the connection is upgraded to a WebSocket connection. The connection closures with mutual acknowledgments will not be retried. If the maximum reconnect attempt is reached, it stops the connection. A `websocket:Client` that retries upon failures is created by providing the `retry` configurations to the client. Use this to re-establish the connection, in cases like the WebSocket client lost the connection due to some transient failure such as a momentary loss of network connectivity or a temporary unavailability of a service. ::: code websocket_retry_client.bal ::: ## Prerequisites - Run the WebSocket service given in the [Send/Receive message](/learn/by-example/websocket-basic-sample/) example. Run the client program by executing the command below. ::: out websocket_retry_client.out ::: ## Related Links - [`websocket` module - API documentation](https://lib.ballerina.io/ballerina/websocket/latest) ================================================ FILE: examples/websocket-retry-client/websocket_retry_client.metatags ================================================ description: BBE on how transient failures can be handled using the WebSocket client. When it comes to WebSocket, the key attribute is maintaining a live connection. This BBE shows how the WebSocket client can be made resilient with its reconnect feature. keywords: ballerina, ballerina by example, bbe, websocket, ws, retry ================================================ FILE: examples/websocket-retry-client/websocket_retry_client.out ================================================ $ bal run websocket_retry_client.bal ================================================ FILE: examples/websocket-service-basic-auth-file-user-store/websocket_service_basic_auth_file_user_store.bal ================================================ import ballerina/websocket; listener websocket:Listener chatListener = new (9090, secureSocket = { key: { certFile: "../resource/path/to/public.crt", keyFile: "../resource/path/to/private.key" } } ); // The service can be secured with Basic authentication and can be authorized optionally. // Using Basic authentication with the file user store can be enabled by setting the // `websocket:FileUserStoreConfig` configurations. // Authorization is based on scopes. A scope maps to one or more groups. // Authorization can be enabled by setting the `string|string[]` type // configurations for `scopes` field. @websocket:ServiceConfig { auth: [ { fileUserStoreConfig: {}, scopes: ["admin"] } ] } service /chat on chatListener { resource function get .() returns websocket:Service { return new ChatService(); } } service class ChatService { *websocket:Service; remote function onMessage(websocket:Caller caller, string chatMessage) returns error? { check caller->writeMessage("Hello, How are you?"); } } ================================================ FILE: examples/websocket-service-basic-auth-file-user-store/websocket_service_basic_auth_file_user_store.md ================================================ # WebSocket service - Basic authentication file user store The `websocket:Service` can be secured with basic authentication and additionally, scopes can be added to enforce authorization. It validates the basic authentication token sent in the `Authorization` header in the initial HTTP request against the provided configurations in the `Config.toml` file. The file stores the usernames and passwords for the authentication and the scopes for the authorization. To engage authentication, set the default values for the `fileUserStoreConfig` field and add the `Config.toml` file next to the service file. To engage authorization, set scopes to the `scopes` field. Both configurations must be given as part of the service configuration. A `401 Unauthorized` response is sent to the client when the authentication fails and a `403 Forbidden` response is sent to the client when the authorization fails. Use this to authenticate and authorize requests based on user stores. ::: code websocket_service_basic_auth_file_user_store.bal ::: >**Info:** As a prerequisite to running the service, populate the `Config.toml` file correctly with the user information as shown below. ::: code Config.toml ::: Run the service by executing the command below. ::: out websocket_service_basic_auth_file_user_store.server.out ::: >**Tip:** You can invoke the above service via the [Basic authentication client](/learn/by-example/websocket-client-basic-auth). ## Related Links - [`websocket` module - API documentation](https://lib.ballerina.io/ballerina/websocket/latest) - [`auth` module - API documentation](https://lib.ballerina.io/ballerina/auth/latest/) - [WebSocket authentication - Specification](/spec/websocket/#52-authentication-and-authorization) ================================================ FILE: examples/websocket-service-basic-auth-file-user-store/websocket_service_basic_auth_file_user_store.metatags ================================================ description: BBE on how to secure WebSocket service with Basic Auth in Ballerina. keywords: ballerina, ballerina by example, bbe, websocket, auth, basic auth ================================================ FILE: examples/websocket-service-basic-auth-file-user-store/websocket_service_basic_auth_file_user_store.server.out ================================================ $ bal run websocket_service_basic_auth_file_user_store.bal ================================================ FILE: examples/websocket-service-basic-auth-ldap-user-store/websocket_service_basic_auth_ldap_user_store.bal ================================================ import ballerina/websocket; listener websocket:Listener chatListener = new (9090, secureSocket = { key: { certFile: "../resource/path/to/public.crt", keyFile: "../resource/path/to/private.key" } } ); // The service can be secured with Basic authentication and can be authorized optionally. // Basic authentication using the LDAP user store can be enabled by setting // the `websocket:LdapUserStoreConfig` configurations. // Authorization is based on scopes. A scope maps to one or more groups. // Authorization can be enabled by setting the `string|string[]` type // configurations for `scopes` field. @websocket:ServiceConfig { auth: [ { ldapUserStoreConfig: { domainName: "avix.lk", connectionUrl: "ldap://localhost:389", connectionName: "cn=admin,dc=avix,dc=lk", connectionPassword: "avix123", userSearchBase: "ou=Users,dc=avix,dc=lk", userEntryObjectClass: "inetOrgPerson", userNameAttribute: "uid", userNameSearchFilter: "(&(objectClass=inetOrgPerson)(uid=?))", userNameListFilter: "(objectClass=inetOrgPerson)", groupSearchBase: ["ou=Groups,dc=avix,dc=lk"], groupEntryObjectClass: "groupOfNames", groupNameAttribute: "cn", groupNameSearchFilter: "(&(objectClass=groupOfNames)(cn=?))", groupNameListFilter: "(objectClass=groupOfNames)", membershipAttribute: "member", userRolesCacheEnabled: true, connectionPoolingEnabled: false, connectionTimeout: 5, readTimeout: 60 }, scopes: ["admin"] } ] } service /chat on chatListener { resource function get .() returns websocket:Service { return new ChatService(); } } service class ChatService { *websocket:Service; remote function onMessage(websocket:Caller caller, string chatMessage) returns error? { check caller->writeMessage("Hello, How are you?"); } } ================================================ FILE: examples/websocket-service-basic-auth-ldap-user-store/websocket_service_basic_auth_ldap_user_store.md ================================================ # WebSocket service - Basic authentication LDAP user store The `websocket:Service` can be secured with basic authentication and additionally, scopes can be added to enforce authorization. It validates the basic authentication token sent in the `Authorization` header with the LDAP server. This server stores the usernames and passwords for the authentication and the scopes for the authorization. To engage authentication, set the LDAP related configurations to the `ldapUserStoreConfig` field. To engage authorization, set scopes to the `scopes` field. Both configurations must be given as part of the service configuration. A `401 Unauthorized` response is sent to the client when the authentication fails, and a `403 Forbidden` response is sent to the client when the authorization fails. Use this to authenticate and authorize requests based on LDAP user stores. ::: code websocket_service_basic_auth_ldap_user_store.bal ::: ## Prerequisites - Run the LDAP server. Run the service by executing the command below. ::: out websocket_service_basic_auth_ldap_user_store.server.out ::: >**Tip:** You can invoke the above service via the [Basic authentication client](/learn/by-example/websocket-client-basic-auth). ## Related Links - [`websocket` module - API documentation](https://lib.ballerina.io/ballerina/websocket/latest) - [`auth` module - API documentation](https://lib.ballerina.io/ballerina/auth/latest/) - [WebSocket authentication - Specification](/spec/websocket/#52-authentication-and-authorization) ================================================ FILE: examples/websocket-service-basic-auth-ldap-user-store/websocket_service_basic_auth_ldap_user_store.metatags ================================================ description: BBE on how to secure WebSocket service with Basic Auth in Ballerina. keywords: ballerina, ballerina by example, bbe, websocket, auth, basic auth ================================================ FILE: examples/websocket-service-basic-auth-ldap-user-store/websocket_service_basic_auth_ldap_user_store.server.out ================================================ $ bal run websocket_service_basic_auth_ldap_user_store.bal ================================================ FILE: examples/websocket-service-error-handling/websocket_service_error_handling.bal ================================================ import ballerina/http; import ballerina/io; import ballerina/websocket; service /chat on new websocket:Listener(9090) { resource function get .(@http:Header string origin) returns websocket:Service|error { if origin != "http://localhost:9090" { // Cancel the handshake by returning an error. return error ("Origin not allowed"); } // Accept the WebSocket upgrade by returning a `websocket:Service`. return new ChatService(); } } service class ChatService { *websocket:Service; remote function onMessage(websocket:Caller caller, string chatMessage) returns error? { io:println(chatMessage); // If an error occurs, the error is returned from the `onMessage`. It is printed on the // server terminal along with the stack trace. check caller->writeMessage("Hello!, How are you?"); } } ================================================ FILE: examples/websocket-service-error-handling/websocket_service_error_handling.md ================================================ # WebSocket service - Error handling The Ballerina `websocket` module allows returning errors from the `get` resource and remote methods. The initial WebSocket upgrade service can do various validations. For instance, origin checking and canceling the WebSocket handshake by returning an `error` from the get resource if the validation fails. In this case, it results in a `400 Bad Request` HTTP response, which is the default response. If the returned error is a `websocket:AuthzError`, or a `websocket:AuthnError`, `403 Forbidden` and `401 Unauthorized` HTTP responses are returned respectively. Once the connection is upgraded to a WebSocket connection, if an error is returned from a remote method in the `websocket:Service`, the error is printed on the server terminal along with the stack-trace and it continues to receive messages. Use `do/fail` block to catch and handle the error when needed. ::: code websocket_service_error_handling.bal ::: Run the service by executing the command below. ::: out websocket_service_error_handling.out ::: >**Tip:** You can invoke the above service via the [WebSocket client](/learn/by-example/websocket-client/). ## Related links - [`websocket` module - API documentation](https://lib.ballerina.io/ballerina/websocket/latest) - [WebSocket service - Specification](/spec/websocket/#3-service-types) - [Check semantics - Example](/learn/by-example/check-semantics/) ================================================ FILE: examples/websocket-service-error-handling/websocket_service_error_handling.metatags ================================================ description: This BBE demonstrates error handling in WebSocket services. keywords: ballerina, ballerina by example, bbe, websocket, service, error handling ================================================ FILE: examples/websocket-service-error-handling/websocket_service_error_handling.out ================================================ $ bal run websocket_service_error_handling.bal error: error occurred at ChatService:onMessage(websocket_service_error_handling.bal:22) ================================================ FILE: examples/websocket-service-jwt-auth/websocket_service_jwt_auth.bal ================================================ import ballerina/websocket; listener websocket:Listener chatListener = new (9090, secureSocket = { key: { certFile: "../resource/path/to/public.crt", keyFile: "../resource/path/to/private.key" } } ); // The service can be secured with JWT authentication and can be authorized // optionally. JWT authentication can be enabled by setting the `websocket:JwtValidatorConfig` configurations. // Authorization is based on scopes. A scope maps to one or more groups. // Authorization can be enabled by setting the `string|string[]` type // configurations for `scopes` field. @websocket:ServiceConfig { auth: [ { jwtValidatorConfig: { issuer: "wso2", audience: "ballerina", signatureConfig: { certFile: "../resource/path/to/public.crt" }, scopeKey: "scp" }, scopes: ["admin"] } ] } service /chat on chatListener { resource function get .() returns websocket:Service { return new ChatService(); } } service class ChatService { *websocket:Service; remote function onMessage(websocket:Caller caller, string chatMessage) returns error? { check caller->writeMessage("Hello, How are you?"); } } ================================================ FILE: examples/websocket-service-jwt-auth/websocket_service_jwt_auth.md ================================================ # WebSocket service - JWT authentication The `websocket:Service` and resource method can be secured with JWT and additionally, scopes can be added to enforce authorization. It validates the JWT sent in the `Authorization` header against the provided configurations. Ballerina uses the concept of scopes for authorization. The scope can be included in the JWT using a custom claim attribute. That custom claim attribute also can be configured as the `scopeKey`. In the authorization phase, the scopes of the service/resource are compared against the scope included in the JWT for at least one match between the two sets. ::: code websocket_service_jwt_auth.bal ::: Run the service by executing the command below. ::: out websocket_service_jwt_auth.server.out ::: >**Tip:** You can invoke the above service via the [self-signed JWT authentication client](/learn/by-example/websocket-client-self-signed-jwt-auth). ## Related Links - [`websocket` module - API documentation](https://lib.ballerina.io/ballerina/websocket/latest) - [`jwt` module - API documentation](https://lib.ballerina.io/ballerina/jwt/latest/) - [WebSocket authentication - Specification](/spec/websocket/#52-authentication-and-authorization) ================================================ FILE: examples/websocket-service-jwt-auth/websocket_service_jwt_auth.metatags ================================================ description: BBE on how to secure WebSocket service with JWT Auth in Ballerina. keywords: ballerina, ballerina by example, bbe, websocket, auth, jwt auth ================================================ FILE: examples/websocket-service-jwt-auth/websocket_service_jwt_auth.server.out ================================================ $ bal run websocket_service_jwt_auth.bal ================================================ FILE: examples/websocket-service-mutual-ssl/websocket_service_mutual_ssl.bal ================================================ import ballerina/http; import ballerina/websocket; // A WebSocket listener can be configured to accept new connections that are // secured via mutual SSL. // The `websocket:ListenerSecureSocket` record provides the SSL-related listener configurations. listener websocket:Listener chatListener = new (9090, secureSocket = { key: { certFile: "../resource/path/to/public.crt", keyFile: "../resource/path/to/private.key" }, // Enables mutual SSL. mutualSsl: { verifyClient: http:REQUIRE, cert: "../resource/path/to/public.crt" } } ); service /chat on chatListener { resource function get .() returns websocket:Service { return new ChatService(); } } service class ChatService { *websocket:Service; remote function onMessage(websocket:Caller caller, string chatMessage) returns error? { check caller->writeMessage("Hello, How are you?"); } } ================================================ FILE: examples/websocket-service-mutual-ssl/websocket_service_mutual_ssl.md ================================================ # WebSocket service - Mutual SSL A `websocket:Listener` with enabled mutual SSL (mTLS) allows you to expose a connection secured with mutual SSL, which is a certificate-based authentication process in which two parties (the client and server) authenticate each other by verifying the digital certificates. It ensures that both parties are assured of each other's identity. A `websocket:Listener` secured with mutual SSL is created by providing the `secureSocket` configurations which require the word `require` as the `verifyClient`, the server's public certificate as the `certFile`, server's private key as the `keyFile` and the client's certificate as the `cert`. Use this to secure the WebSocket connection over mutual SSL. ::: code websocket_service_mutual_ssl.bal ::: Run the service by executing the command below. ::: out websocket_service_mutual_ssl.server.out ::: >**Tip:** You can invoke the above service via the [Mutual SSL/TLS client](/learn/by-example/websocket-client-mutual-ssl/). ## Related Links - [`websocket` module - API documentation](https://lib.ballerina.io/ballerina/websocket/latest) - [WebSocket SSL/TLS - Specification](/spec/websocket/#5-securing-the-websocket-connections) ================================================ FILE: examples/websocket-service-mutual-ssl/websocket_service_mutual_ssl.metatags ================================================ description: BBE on how to secure WebSocket listener with mutual SSL. keywords: ballerina, ballerina by example, bbe, websocket, mutual ssl, ssl protocols, ciphers ================================================ FILE: examples/websocket-service-mutual-ssl/websocket_service_mutual_ssl.server.out ================================================ $ bal run websocket_service_mutual_ssl.bal ================================================ FILE: examples/websocket-service-oauth2/websocket_service_oauth2.bal ================================================ import ballerina/websocket; listener websocket:Listener chatListener = new (9090, secureSocket = { key: { certFile: "../resource/path/to/public.crt", keyFile: "../resource/path/to/private.key" } } ); // The service can be secured with OAuth2 and by enforcing authorization // optionally. It can be enabled by setting the `websocket:OAuth2IntrospectionConfig` configurations. // Authorization is based on scopes. A scope maps to one or more groups. // Authorization can be enabled by setting the `string|string[]` type configurations for `scopes` field. @websocket:ServiceConfig { auth: [ { oauth2IntrospectionConfig: { url: "https://localhost:9445/oauth2/introspect", tokenTypeHint: "access_token", scopeKey: "scp", clientConfig: { customHeaders: {"Authorization": "Basic YWRtaW46YWRtaW4="}, secureSocket: { cert: "../resource/path/to/public.crt" } } }, scopes: ["admin"] } ] } service /chat on chatListener { resource function get .() returns websocket:Service { return new ChatService(); } } service class ChatService { *websocket:Service; remote function onMessage(websocket:Caller caller, string chatMessage) returns error? { check caller->writeMessage("Hello, How are you?"); } } ================================================ FILE: examples/websocket-service-oauth2/websocket_service_oauth2.md ================================================ # WebSocket service - OAuth2 The `websocket:Service` and resource method can be secured with OAuth2 and additionally, scopes can be added to enforce fine-grained authorization. It validates the OAuth2 token sent in the `Authorization` header against the provided configurations. This calls the configured introspection endpoint to validate. Ballerina uses the concept of scopes for authorization. The scope can be included in the introspection response using a custom claim attribute. That custom claim attribute also can be configured as the `scopeKey`. In the authorization phase, the scopes of the service/resource are compared against the scope included in the introspection response for at least one match between the two sets. ::: code websocket_service_oauth2.bal ::: Run the service by executing the command below. ::: out websocket_service_oauth2.server.out ::: >**Tip:** You can invoke the above service via the [OAuth2 JWT Bearer grant type client](/learn/by-example/websocket-client-oauth2-jwt-bearer-grant-type). ## Related Links - [`websocket` module - API documentation](https://lib.ballerina.io/ballerina/websocket/latest) - [`oauth2` module - API documentation](https://lib.ballerina.io/ballerina/oauth2/latest/) - [WebSocket authentication - Specification](/spec/websocket/#52-authentication-and-authorization) ================================================ FILE: examples/websocket-service-oauth2/websocket_service_oauth2.metatags ================================================ description: BBE on how to secure WebSocket service with OAuth2 in Ballerina. keywords: ballerina, ballerina by example, bbe, websocket, auth, oauth2, introspection ================================================ FILE: examples/websocket-service-oauth2/websocket_service_oauth2.server.out ================================================ $ bal run websocket_service_oauth2.bal ================================================ FILE: examples/websocket-service-payload-constraint-validation/websocket_service_payload_constraint_validation.bal ================================================ import ballerina/constraint; import ballerina/io; import ballerina/websocket; // Add a constraint for the maximum length. @constraint:String { maxLength: 20 } public type Chat string; service /chat on new websocket:Listener(9090) { resource function get .() returns websocket:Service { return new ChatService(); } } service class ChatService { *websocket:Service; remote function onMessage(websocket:Caller caller, Chat chatMessage) returns error? { io:println(chatMessage); check caller->writeMessage("Hello!, How are you?"); } } ================================================ FILE: examples/websocket-service-payload-constraint-validation/websocket_service_payload_constraint_validation.md ================================================ # WebSocket service - Payload constraint validation The Ballerina constraint package allows you to add additional constraints to the received payload. Through service payload constraint validation, the payload can be validated according to the defined constraints. The constraint validation happens along with the data binding step in the remote method signature parameter. Constraints can be added to a given data type using different annotations. If the validation fails, the `onError` remote method is dispatched with the validation error details. Use this to validate the receiving payload, which allows you to guard against unnecessary processing and malicious payloads. ::: code websocket_service_payload_constraint_validation.bal ::: Run the service by executing the command below. ::: out websocket_service_payload_constraint_validation.out ::: >**Tip:** You can invoke the above service via the [WebSocket client](/learn/by-example/websocket-client/). ## Related links - [`websocket` module - API documentation](https://lib.ballerina.io/ballerina/websocket/latest) - [WebSocket service - Specification](/spec/websocket/#3-service-types) - [Constraint BBE](/learn/by-example/constraint-validations/) ================================================ FILE: examples/websocket-service-payload-constraint-validation/websocket_service_payload_constraint_validation.metatags ================================================ description: BBE demonstrates the constraint validation capability of the WebSocket client. keywords: ballerina, ballerina by example, bbe, websocket, data binding, client, constraint, validation ================================================ FILE: examples/websocket-service-payload-constraint-validation/websocket_service_payload_constraint_validation.out ================================================ $ bal run websocket_client_payload_constraint_validation.bal Hello!, How are you? ================================================ FILE: examples/websocket-service-ssl-tls/websocket_service_ssl_tls.bal ================================================ import ballerina/websocket; // A WebSocket listener can be configured to communicate through WSS as well. // To secure a listener using SSL/TLS, the listener needs to be configured with // a certificate file and a private key file for the listener. // The `websocket:ListenerSecureSocket` record // provides the SSL-related listener configurations of the listener. listener websocket:Listener chatListener = new (9090, secureSocket = { key: { certFile: "../resource/path/to/public.crt", keyFile: "../resource/path/to/private.key" } } ); service /chat on chatListener { resource function get .() returns websocket:Service { return new ChatService(); } } service class ChatService { *websocket:Service; remote function onMessage(websocket:Caller caller, string chatMessage) returns error? { check caller->writeMessage("Hello, How are you?"); } } ================================================ FILE: examples/websocket-service-ssl-tls/websocket_service_ssl_tls.md ================================================ # WebSocket service - SSL/TLS The `websocket:Listener` configured with SSL/TLS allows you to expose a connection secured with one-way SSL/TLS. A `websocket:Listener` secured with TLS/SSL is created by providing the `secureSocket` configurations which require the server's public certificate as the `certFile` and the server's private key as the `keyFile`. Use this to expose a WSS connection. ::: code websocket_service_ssl_tls.bal ::: Run the service by executing the command below. ::: out websocket_service_ssl_tls.server.out ::: >**Tip:** You can invoke the above service via the [SSL/TLS client](/learn/by-example/websocket-client-ssl-tls/). ## Related Links - [`websocket` module - API documentation](https://lib.ballerina.io/ballerina/websocket/latest) - [WebSocket SSL/TLS - Specification](/spec/websocket/#5-securing-the-websocket-connections) ================================================ FILE: examples/websocket-service-ssl-tls/websocket_service_ssl_tls.metatags ================================================ description: BBE on how to secure WebSocket listener with SSL. keywords: ballerina, ballerina by example, bbe, websocket, wss, ssl, tls ================================================ FILE: examples/websocket-service-ssl-tls/websocket_service_ssl_tls.server.out ================================================ $ bal run websocket_service_ssl_tls.bal ================================================ FILE: examples/websocket-text-client/websocket_text_client.md ================================================ # Handle text messages with client The WebSocket client can be used to connect to and interact with a WebSocket server in a Synchronous manner. This example demonstrates how to read and write text messages using ballerina websocket client. For more information on the underlying module, see the [`websocket` module](https://lib.ballerina.io/ballerina/websocket/latest/). ::: code ./examples/websocket-text-client/websocket_text_client.bal ::: ::: out ./examples/websocket-text-client/websocket_text_client.out ::: ================================================ FILE: examples/websocket-timeout-client/websocket_timeout_client.bal ================================================ import ballerina/websocket; public function main() returns error? { // Set the handshake timeout for 5 seconds. websocket:Client chatClient = check new ("ws://localhost:9090/chat", handShakeTimeout = 5); check chatClient->writeMessage("Hello John!"); } ================================================ FILE: examples/websocket-timeout-client/websocket_timeout_client.md ================================================ # WebSocket client - Timeout The `websocket:Client` configured with timeouts can gracefully handle timeouts that happen in the running applications. Handshake timeout is the time (in seconds) that a connection waits to get the response of the WebSocket handshake. If the timeout exceeds, then, the connection is terminated with a `HandshakeTimedOut` error. If the configuration value `< 0`, then the value sets to the default value(300). Apart from the `handShakeTimeout`, the WebSocket client also has `readTimeout` and `writeTimeout` configurations to configure idle time to wait to receive a message and to write a message respectively. To enable handshake timeout, provide the `handShakeTimeout` configuration to the client. Use the handshake timeout to gracefully handle response delays that could occur due to network problems or problems in the back end. ::: code websocket_timeout_client.bal ::: ## Prerequisites - Run the WebSocket service given in the [Send/Receive message](/learn/by-example/websocket-basic-sample/) example. Run the client program by executing the command below. ::: out websocket_timeout_client.out ::: ## Related Links - [`websocket` module - API documentation](https://lib.ballerina.io/ballerina/websocket/latest) ================================================ FILE: examples/websocket-timeout-client/websocket_timeout_client.metatags ================================================ description: BBE on how to use a timeout in Ballerina WebSocket client. keywords: ballerina, ballerina by example, bbe, websocket, ws, timeout ================================================ FILE: examples/websocket-timeout-client/websocket_timeout_client.out ================================================ $ bal run websocket_timeout_client.bal ================================================ FILE: examples/websub-webhook-sample/websub_webhook_sample.bal ================================================ // The Ballerina WebSub Subscriber service, which could be used as a WebHook Listener for GitHub. import ballerina/io; import ballerina/websub; // Annotation-based configurations specifying the subscription parameters. @websub:SubscriberServiceConfig { target: [ "https://api.github.com/hub", "https://github.com///events/push.json" ], secret: "", httpConfig: { auth: { token: "" } } } service on new websub:Listener(9090) { // Defines the remote method that accepts the event notification request for the WebHook. remote function onEventNotification(websub:ContentDistributionMessage event) returns error? { json retrievedContent = check event.content.ensureType(); if retrievedContent.zen is string { int hookId = check retrievedContent.hook_id; io:println(string `PingEvent received for webhook [${hookId}]`); int senderId = check retrievedContent.sender.id; io:println(string `Event sender [${senderId}]`); } else if retrievedContent.ref is string { string repositoryName = check retrievedContent.repository.name; io:println(string `PushEvent received for [${repositoryName}]`); string lastUpdatedTime = check retrievedContent.repository.updated_at; io:println(string `Last updated at ${lastUpdatedTime}`); } } } ================================================ FILE: examples/websub-webhook-sample/websub_webhook_sample.md ================================================ # WebSub service - Consume github events GitHub webhooks provide the capability to receive notifications based on the events in a GitHub repository. The Ballerina `websub` module can be used to define websub-compliant webhooks which are used to receive notifications from any websub-compliant `hub` implementation. Specify the GitHub pubsubhub API URL and the relevant event URL as the `target` parameter in `@websub:SubscriberServiceConfig` annotation. Start the `websub:SubscriberService` to receive event notifications. ::: code websub_webhook_sample.bal ::: ## Prerequisites - Internet connectivity to connect to github API. - An active github repository to receive relevant events. Run the subscriber service by executing the following command. ::: out websub_webhook_sample.out ::: ## Related links - [`websub` module - API documentation](https://lib.ballerina.io/ballerina/websub/latest/) - [Websub subscriber service - Specification](https://ballerina.io/spec/websub/#22-subscriber-service) ================================================ FILE: examples/websub-webhook-sample/websub_webhook_sample.metatags ================================================ description: This BBE demonstrates how a WebSub Subscriber service could be registered as a WebHook Service for GitHub. keywords: ballerina, ballerina by example, bbe, websub, subscriber ================================================ FILE: examples/websub-webhook-sample/websub_webhook_sample.out ================================================ $ bal run websub_webhook_sample.bal time = 2022-01-24T17:48:07.207+05:30 level = WARN module = ballerina/websub message = "HTTPS is recommended but using HTTP" PingEvent received for webhook [287075824] Event sender [77491511] PushEvent received for [Hello-World] Last updated at 2021-03-15T15:43:01Z ================================================ FILE: examples/while-statement/while_statement.bal ================================================ import ballerina/io; public function main() { // Loop through a list string[] names = ["Bob", "Jo", "Ann", "Tom"]; int i = 0; while i < names.length() { io:println(names[i]); i += 1; } } ================================================ FILE: examples/while-statement/while_statement.md ================================================ # While statement The `while` statement can be used to repeat a block of statements until a boolean condition is true. The parentheses `( )` around the boolean expression are not required. However, it is required to enclose the statement block using curly braces `{ }`. ::: code while_statement.bal ::: ::: out while_statement.out ::: ## Related links - [Break statement](/learn/by-example/break-statement/) - [Continue statement](/learn/by-example/continue-statement/) - [Foreach statement](/learn/by-example/foreach-statement/) ================================================ FILE: examples/while-statement/while_statement.metatags ================================================ description: This BBE demonstrates how to use the `while` statement to loop through a list in Ballerina. keywords: ballerina, ballerina by example, bbe, while, loop, repeat, iterate ================================================ FILE: examples/while-statement/while_statement.out ================================================ $ bal run while_statement.bal Bob Jo Ann Tom ================================================ FILE: examples/wildcard-binding-pattern/wildcard_binding_pattern.bal ================================================ public function main() { // Since the basic type of the value returned by `getInt()` is `int` // which belongs to `any` type, this will not cause an assignment. int _ = getInt(); // The type of the wildcard binding pattern is derived as `any` // since it results in a successful match. _ = 3; } function getInt() returns int { return 0; } ================================================ FILE: examples/wildcard-binding-pattern/wildcard_binding_pattern.md ================================================ # Wildcard binding pattern A wildcard binding pattern which is denoted by the `_` symbol, matches a value if the value belongs to type `any`. As long as the basic type of the value is not an `error`, it does not cause any assignment to be made to the variable. It is useful for ignoring values such as the return value of a function or values in a list. ::: code wildcard_binding_pattern.bal ::: ::: out wildcard_binding_pattern.out ::: ## Related links - [Binding patterns](/learn/by-example/binding-patterns/) ================================================ FILE: examples/wildcard-binding-pattern/wildcard_binding_pattern.metatags ================================================ description: This BBE introduces unused variables, ignoring the return value of a function and matching a value belonging to type any keywords: ballerina, ballerina by example, bbe, binding pattern, wildcard binding pattern ================================================ FILE: examples/wildcard-binding-pattern/wildcard_binding_pattern.out ================================================ $ bal run wildcard_binding_pattern.bal ================================================ FILE: examples/xml-access/xml_access.bal ================================================ import ballerina/io; public function main() returns error? { xml x1 = xml `Sherlock Holmes
Sir Arthur Conan Doyle English
`; // `x[i]` or `x.get(i)` gives the `i`-th item. io:println(x1[0]); // `x.id` accesses a required attribute named `id`: the result is an `error` if there is no such // attribute or if `x` is not a singleton. xml x2 = xml `Hello`; string id = check x2.id; io:println(id); // `x?.id` accesses an optional attribute named `id`: the result is `()` if there is no such // attribute. string? name = check x2?.name; // The following nil check returns true since the attribute `name` does not exist in the `x2`. io:println(name is ()); } ================================================ FILE: examples/xml-access/xml_access.md ================================================ # XML access It is possible to access elements in XML. - `x[i]` gives the `i`-th item (empty sequence if none). - `x.id` accesses the required attribute named `id`: the result is an error if there is no such attribute or if `x` is not a singleton. - `x?.id` accesses an optional attribute named `id`: the result is `()` if there is no such attribute. The `lang.xml` langlib provides the other operations. ::: code xml_access.bal ::: ::: out xml_access.out ::: ## Related links - [XML data model](/learn/by-example/xml-data-model/) - [XML operations](/learn/by-example/xml-operations/) - [`lang.xml` - Module documentation](https://lib.ballerina.io/ballerina/lang.xml/latest/) ================================================ FILE: examples/xml-access/xml_access.metatags ================================================ description: This BBE demonstrates how to get an item in XML, access an attribute in XML, and access XML optional attributes in Ballerina. keywords: ballerina, ballerina by example, bbe, xml data model, xml, get element ================================================ FILE: examples/xml-access/xml_access.out ================================================ $ bal run xml_access.bal Sherlock Holmes greeting true ================================================ FILE: examples/xml-data-model/xml_data_model.bal ================================================ import ballerina/io; public function main() returns error? { // An XML element. There can be only one root element. xml x1 = xml `The Lost World`; io:println(x1); // An XML processing instruction. xml x2 = xml ``; io:println(x2); // An XML comment. xml x3 = xml ``; io:println(x3); // An XML text. xml x4 = xml `Hello, world!`; io:println(x4); // `xml:createText` can be used to convert a string to `xmlText`. string hello = "Hello"; string world = "World"; xml:Text xmlString = xml:createText(hello + " " + world); io:println(xmlString); // Creates an XML value. xml xmlValue = xml `Sherlock Holmes
Sir Arthur Conan Doyle English
`; // `x[i]` or `x.get(i)` gives the `i`-th item. io:println(xmlValue[0]); // `x.id` accesses a required attribute named `id`. The result is an `error` if there is no such // attribute or if `x` is not a singleton. xml xmlHello = xml `Hello`; string id = check xmlHello.id; io:println(id); // `x?.id` accesses an optional attribute named `id`. The result is `()` if there is no such // attribute. string? name = check xmlHello?.name; io:println(name); xml xmlItems = xml ` A Study in Scarlet Arthur Conan Doyle Daily Plannerday365 The Sign of Four Arthur Conan Doyle markerblue `; // `x.` - retrieves every element in `x` named `items`. xml items = xmlItems.; io:println(items); } ================================================ FILE: examples/xml-data-model/xml_data_model.md ================================================ # XML data model An `xml` value is a sequence representing the parsed content of an XML element. An `xml` value has four kinds of items. - The element, processing instruction, and comment singletons correspond directly to the items in the XML information set - The text item corresponds to one or more character information items An XML document is an `xml` sequence with only one `element` and no `text`. An `element` item is mutable and consists of: - `name`: type `string` - `attributes`: type `map` - `children`: type `xml` A `text` item is immutable. - it has no identity: `==` is the same as `===` - consecutive `text` items never occur in an `xml` value: they are always merged ::: code xml_data_model.bal ::: ::: out xml_data_model.out ::: ## Related links - [XML operations](/learn/by-example/xml-operations/) - [`lang.xml` - Module documentation](https://lib.ballerina.io/ballerina/lang.xml/latest/) ================================================ FILE: examples/xml-data-model/xml_data_model.metatags ================================================ description: This BBE demonstrates how to define XML elements, XML processing instructions, XML comments, XML text, convert string to XML, create XML value, get the items, access the XML attributes, and retrieve elements in Ballerina. keywords: ballerina, ballerina by example, bbe, xml data model, xml ================================================ FILE: examples/xml-data-model/xml_data_model.out ================================================ $ bal run xml_data_model.bal The Lost World Hello, world! Hello World Sherlock Holmes greeting A Study in Scarlet Arthur Conan Doyle Daily Plannerday365 The Sign of Four Arthur Conan Doyle markerblue ================================================ FILE: examples/xml-from-json-conversion/xml_from_json_conversion.bal ================================================ import ballerina/data.xmldata; import ballerina/io; public function main() returns error? { // Creates a JSON value. json jsonValue = {"Store": { "@id": "AST", "name": "Anne", "address": { "street": "Main", "city": "94" }, "codes": ["4", "8"] }}; // Converts the JSON value to XML using a default `attributePrefix` (i.e., the `@` character). xml xmlValue = check xmldata:fromJson(jsonValue); io:println(xmlValue); } ================================================ FILE: examples/xml-from-json-conversion/xml_from_json_conversion.md ================================================ # JSON to XML conversion The `data.xmldata` library provides an API to perform conversions from JSON to XML. For more information on the underlying module, see the [`data.xmldata` module](https://lib.ballerina.io/ballerina/data.xmldata/latest/). ::: code xml_from_json_conversion.bal ::: To run this sample, use the `bal run` command. ::: out xml_from_json_conversion.out ::: ================================================ FILE: examples/xml-from-json-conversion/xml_from_json_conversion.metatags ================================================ description: This BBE demonstrates the conversion from JSON to XML in Ballerina. keywords: ballerina, ballerina by example, BBE, json, xml ================================================ FILE: examples/xml-from-json-conversion/xml_from_json_conversion.out ================================================ $ bal run xml_from_json_conversion.bal Anne
Main94
48
================================================ FILE: examples/xml-from-record-conversion/xml_from_record_conversion.bal ================================================ import ballerina/data.xmldata; import ballerina/io; // Defines a record type with annotations. @xmldata:Namespace { prefix: "ns", uri: "http://sdf.com" } type Invoice record { int id; Item[] items; @xmldata:Attribute string 'xmlns = "example.com"; @xmldata:Attribute string status?; }; @xmldata:Namespace { uri: "http://example1.com" } type Item record { string itemCode; int count; }; public function main() returns error? { // Creates an `Invoice` record. Invoice data = { id: 1, items: [ {itemCode: "223345", count: 1}, {itemCode: "223300", count: 7} ], status: "paid" }; // Converts a `record` representation to its XML representation. xml result = check xmldata:toXml(data); io:println(result); } ================================================ FILE: examples/xml-from-record-conversion/xml_from_record_conversion.md ================================================ # Record to XML conversion The `data.xmldata` library provides APIs to perform the conversion from a Ballerina record to XML. For more information on the underlying module, see the [`data.xmldata` module](https://lib.ballerina.io/ballerina/data.xmldata/latest/). ::: code xml_from_record_conversion.bal ::: To run this sample, use the `bal run` command. ::: out xml_from_record_conversion.out ::: ================================================ FILE: examples/xml-from-record-conversion/xml_from_record_conversion.metatags ================================================ description: This BBE demonstrates the conversion from record to XML in Ballerina. keywords: ballerina, ballerina by example, BBE, record, xml ================================================ FILE: examples/xml-from-record-conversion/xml_from_record_conversion.out ================================================ $ bal run xml_from_record_conversion.bal 122334512233007 ================================================ FILE: examples/xml-iteration/xml_iteration.bal ================================================ import ballerina/io; public function main() { xml x1 = xml `Sherlock Holmes
Sir Arthur Conan Doyle English
`; // `foreach` iterates over each item. foreach var item in x1 { io:println(item); } // The `forEach()` function available in the `lang.xml` library iterates over each item. x1.forEach((e) => io:println(e)); } ================================================ FILE: examples/xml-iteration/xml_iteration.md ================================================ # XML iteration The `foreach` statement and `.forEach()` function available in the `lang.xml` library iterate the XML over each item. ::: code xml_iteration.bal ::: ::: out xml_iteration.out ::: ## Related links - [XML data model](/learn/by-example/xml-data-model/) - [XML operations](/learn/by-example/xml-operations/) - [Foreach statement](/learn/by-example/foreach-statement/) - [`lang.xml` - Module documentation](https://lib.ballerina.io/ballerina/lang.xml/latest/) ================================================ FILE: examples/xml-iteration/xml_iteration.metatags ================================================ description: This BBE demonstrates how to iterate over each item in XML in Ballerina. keywords: ballerina, ballerina by example, bbe, xml iteration, forEach ================================================ FILE: examples/xml-iteration/xml_iteration.out ================================================ $ bal run xml_iteration.bal Sherlock Holmes
Sir Arthur Conan Doyle English
Sherlock Holmes
Sir Arthur Conan Doyle English
================================================ FILE: examples/xml-mutation/xml_mutation.bal ================================================ import ballerina/io; public function main() { xml:Element x1 = xml `
Sir Arthur Conan Doyle English
`; // Sets the children of an XML element. // This panics if it would result in the element structure becoming cyclic. x1.setChildren(xml `French`); io:println(x1); // Changes the name of an XML element. x1.setName("updatedDetails"); io:println(x1); } ================================================ FILE: examples/xml-mutation/xml_mutation.md ================================================ # XML mutation It is possible to mutate XML elements using functions available in the `lang.xml`. ::: code xml_mutation.bal ::: ::: out xml_mutation.out ::: ## Related links - [XML data model](/learn/by-example/xml-data-model/) - [XML operations](/learn/by-example/xml-operations/) - [`lang.xml` - Module documentation](https://lib.ballerina.io/ballerina/lang.xml/latest/) ================================================ FILE: examples/xml-mutation/xml_mutation.metatags ================================================ description: This BBE demonstrates how to update XML values. keywords: ballerina, ballerina by example, bbe, xml, set children, change xml name ================================================ FILE: examples/xml-mutation/xml_mutation.out ================================================ $ bal run xml_mutation.bal
French
French ================================================ FILE: examples/xml-namespaces/xml_namespaces.bal ================================================ import ballerina/io; public function main() { // The `p:e` qualified name, where `http://example.com/` is the namespace name bound to `p`, // is expanded into `{http://example.com/}e`. xml:Element e = xml ``; string name = e.getName(); io:println(name); } ================================================ FILE: examples/xml-namespaces/xml_namespaces.md ================================================ # XML namespaces The goal is to support namespaces without adding complexity if you don’t use them. The `ns:x` qualified name in XML is expanded into `{url}x` where `url` is the namespace name bound to `ns`. The XML namespace declarations are kept as attributes using the standard binding of [xmlns](http://www.w3.org/2000/xmlns/). ::: code xml_namespaces.bal ::: ::: out xml_namespaces.out ::: ## Related links - [XML data model](/learn/by-example/xml-data-model/) ================================================ FILE: examples/xml-namespaces/xml_namespaces.metatags ================================================ description: This BBE demonstrates xml namespaces in Ballerina. keywords: ballerina, ballerina by example, bbe, xml namespace ================================================ FILE: examples/xml-namespaces/xml_namespaces.out ================================================ $ bal run xml_namespaces.bal {http://example.com/}e ================================================ FILE: examples/xml-navigation/xml_navigation.bal ================================================ import ballerina/io; public function main() { xml x = xml ` A Study in Scarlet Arthur Conan Doyle Daily Plannerday365 The Sign of Four Arthur Conan Doyle markerblue `; // `x.` - retrieves every element in `x` named `items`. xml a = x.; io:println(a); // `x/*` - for every element `e` in `x`, retrieves the children of `e`. xml b = x/*; io:println(b); // `x/` - for every element `e` in `x`, retrieves every element named `planner` // in the children of `e`. xml c = x/; io:println(c); // `x/` - for every element `e` in `x`, retrieves every element named // `planner` or `pen` in the children of `e`. xml d = x/; io:println(d); // `x/<*>` - for every element `e` in `x`, retrieves every element in the children of `e`. xml e = x/<*>; io:println(e); // `x/**/` - for every element `e` in `x`, retrieves every element named `name` in // the descendants of `e`. xml f = x/**/; io:println(f); // `x/[0]` - for every element `e` in `x`, retrieves the first element named `book` in // the children of `e`. xml g = x/[0]; io:println(g); } ================================================ FILE: examples/xml-navigation/xml_navigation.md ================================================ # XML navigation XML navigation expressions allow for convenient navigation of XML element structure, in a similar way to XPath. ::: code xml_navigation.bal ::: ::: out xml_navigation.out ::: ## Related links - [XML data model](/learn/by-example/xml-data-model/) - [XML operations](/learn/by-example/xml-operations/) - [XML access](/learn/by-example/xml-access/) ================================================ FILE: examples/xml-navigation/xml_navigation.metatags ================================================ description: This BBE demonstrates XML navigation keywords: ballerina, ballerina by example, bbe, xml, xml navigation ================================================ FILE: examples/xml-navigation/xml_navigation.out ================================================ $ bal run xml_navigation.bal A Study in Scarlet Arthur Conan Doyle Daily Plannerday365 The Sign of Four Arthur Conan Doyle markerblue A Study in Scarlet Arthur Conan Doyle Daily Plannerday365 The Sign of Four Arthur Conan Doyle markerblue Daily Plannerday365 Daily Plannerday365markerblue A Study in Scarlet Arthur Conan Doyle Daily Plannerday365 The Sign of Four Arthur Conan Doyle markerblue A Study in ScarletArthur Conan DoyleThe Sign of FourArthur Conan Doyle A Study in Scarlet Arthur Conan Doyle ================================================ FILE: examples/xml-operations/xml_operations.bal ================================================ import ballerina/io; public function main() { xml x1 = xml `Sherlock Holmes`; xml:Element x2 = xml `
Sir Arthur Conan Doyle English
`; // '+' or `x1.concat(x2)` can be used for concatenation. xml x3 = x1 + x2; io:println(x3); xml x4 = xml `Sherlock Holmes
Sir Arthur Conan Doyle English
`; // `==` does a deep equality check. boolean eq = x3 == x4; io:println(eq); } ================================================ FILE: examples/xml-operations/xml_operations.md ================================================ # XML operations In XML, It is possible to perform concatenation and deep equals checks. ::: code xml_operations.bal ::: ::: out xml_operations.out ::: ## Related links - [XML data model](/learn/by-example/xml-data-model/) - [`lang.xml` - Module documentation](https://lib.ballerina.io/ballerina/lang.xml/latest/) ================================================ FILE: examples/xml-operations/xml_operations.metatags ================================================ description: This BBE demonstrates XML concatenation and how to use deep equality checks for XML in Ballerina. keywords: ballerina, ballerina by example, bbe, xml, xml operations ================================================ FILE: examples/xml-operations/xml_operations.out ================================================ $ bal run xml_operations.bal Sherlock Holmes
Sir Arthur Conan Doyle English
false ================================================ FILE: examples/xml-subtyping/xml_subtyping.bal ================================================ import ballerina/io; public function main() { // An `xml` value belongs to an `xml:Element` if it consists of just an element item. xml:Element firstElement = xml `

Hello

`; // Similarly, a value belongs to the `xml:Comment` or `xml:ProcessingInstruction` if it // consists of just a comment item or a processing instruction item. xml:Comment comment = xml ``; io:println(comment); xml:ProcessingInstruction procInst = xml ``; io:println(procInst); // An `xml` value belongs to the `xml:Text` if it consists of only a text item or is empty. xml:Text empty = xml ``; io:println(empty); xml:Text text = xml `Hello World`; io:println(text); string hello = "Hello"; string world = "World"; // `xml:createText` can be used to convert a string to `xmlText`. xml:Text xmlString = xml:createText(hello + " " + world); io:println(xmlString); xml:Element secondElement = xml `World`; // Concatenating multiple items results in a sequence of items (`xml`). // The concatenation below will result in a sequence of XML elements (`xml`). xml resultElement = firstElement + secondElement; io:println(resultElement is xml); xml xmlHelloWorld = xml `

hello

World`; // An `xml` value belongs to the `xml` type if each of its members belongs to type `T`. io:println(xmlHelloWorld is xml); io:println(resultElement); rename(resultElement, "q", "r"); io:println(resultElement); } // Functions in `lang.xml` use subtyping to provide safe and convenient typing. // For example, `x.elements()` returns element items in `x` as type // `xml` and `e.getName()` and `e.setName()` are defined when // `e` has type `xml:Element`. function rename(xml x, string oldName, string newName) { foreach xml:Element e in x.elements() { if e.getName() == oldName { e.setName(newName); } rename(e.getChildren(), oldName, newName); } } ================================================ FILE: examples/xml-subtyping/xml_subtyping.md ================================================ # XML subtyping - An `xml` value belongs to `xml:Element` if it consists of just an element item. Similarly for `xml:Comment` and `xml:ProcessingInstruction`. - An `xml` value belongs to `xml:Text` if it consists of a text item or is empty. - An `xml` value belongs to the type `xml` if each of its members belong to `T`. Functions in `lang.xml` use this to provide safe and convenient typing. For example, `x.elements()` returns element items in `x` as type `xml` and `e.getName()` and `e.setNam ()` are defined when `e` has type `xml:Element`. ::: code xml_subtyping.bal ::: ::: out xml_subtyping.out ::: ## Related links - [XML data model](/learn/by-example/xml-data-model/) - [XML operations](/learn/by-example/xml-operations/) - [`lang.xml` - Module documentation](https://lib.ballerina.io/ballerina/lang.xml/latest/) ================================================ FILE: examples/xml-subtyping/xml_subtyping.metatags ================================================ description: This BBE demonstrates XML subtyping in Ballerina. keywords: ballerina, ballerina by example, bbe, xml, xml subtyping ================================================ FILE: examples/xml-subtyping/xml_subtyping.out ================================================ $ bal run xml_subtyping.bal Hello World Hello World true false

Hello

World

Hello

World ================================================ FILE: examples/xml-templates/xml_templates.bal ================================================ import ballerina/io; string url = "https://ballerina.io"; // `xml` values can be constructed using an XML template expression. // Attribute values can have `string` values as interpolated expressions. xml content = xml `Ballerina is an exciting new language!`; // Interpolated expressions can also be in content (`xml` or `string` values). xml p = xml `

${content}

`; public function main() { io:println(content); io:println(p); } ================================================ FILE: examples/xml-templates/xml_templates.md ================================================ # XML templates `xml` values can be constructed using an XML template expression. Phase 2 processing for `xml` template tag parses strings using the XML 1.0 Recommendation's grammar for content (what XML allows between a start-tag and an end-tag). Interpolated expressions can be in content (`xml` or `string` values) or in attribute values (`string` values). ::: code xml_templates.bal ::: ::: out xml_templates.out ::: ================================================ FILE: examples/xml-templates/xml_templates.metatags ================================================ description: This BBE demonstrates XML templates in Ballerina. keywords: ballerina, ballerina by example, bbe, xml, xml template ================================================ FILE: examples/xml-templates/xml_templates.out ================================================ $ bal run xml_templates.bal Ballerina is an exciting new language!

Ballerina is an exciting new language!

================================================ FILE: examples/xml-templates-and-query/xml_templates_and_query.bal ================================================ import ballerina/io; type Person record {| string name; string country; |}; function personsToXml(Person[] persons) returns xml { // Uses a template containing a query expression, which also contains a template. return xml`${from var {name, country} in persons select xml`${name}`}`; } public function main() { Person[] persons = [ {name: "Jane", country: "USA"}, {name: "Mike", country: "Germany"} ]; io:println(personsToXml(persons)); } ================================================ FILE: examples/xml-templates-and-query/xml_templates_and_query.md ================================================ # XML templates and query XML templates can be combined with queries. It is possible to have a template containing a query expression, which also contains a template. ::: code xml_templates_and_query.bal ::: ::: out xml_templates_and_query.out ::: ## Related links - [XML data model](/learn/by-example/xml-data-model/) - [Query expressions](/learn/by-example/query-expressions/) - [Iterating over xml with query](/learn/by-example/iterating-over-xml-with-query/) ================================================ FILE: examples/xml-templates-and-query/xml_templates_and_query.metatags ================================================ description: This BBE demonstrates the use of XML templates and queries in Ballerina. keywords: ballerina, ballerina by example, bbe, xml templates, query ================================================ FILE: examples/xml-templates-and-query/xml_templates_and_query.out ================================================ $ bal run xml_templates_and_query.bal JaneMike ================================================ FILE: examples/xml-to-json-conversion/xml_to_json_conversion.bal ================================================ import ballerina/data.xmldata; import ballerina/io; // Defines a record type to represent the XML structure. type Address record { string street; string city; }; type Store record { string name; Address address; string[] codes; @xmldata:Attribute string id; }; public function main() returns error? { // Creates an XML value. xml xmlValue = xml ` Anne
Main 94
4 8
`; // Converts the XML value to a record type. Store store = check xmldata:parseAsType(xmlValue); // Converts the record value to a JSON value. json jsonValue = store.toJson(); io:println(jsonValue); } ================================================ FILE: examples/xml-to-json-conversion/xml_to_json_conversion.md ================================================ # XML to JSON conversion The `data.xmldata` library provides APIs to convert XML to a Ballerina record, which can then be converted to JSON. For more information on the underlying module, see the [`data.xmldata` module](https://lib.ballerina.io/ballerina/data.xmldata/latest/). ::: code xml_to_json_conversion.bal ::: To run this sample, use the `bal run` command. ::: out xml_to_json_conversion.out ::: ================================================ FILE: examples/xml-to-json-conversion/xml_to_json_conversion.metatags ================================================ description: This BBE demonstrates the conversion from XML to JSON in Ballerina. keywords: ballerina, ballerina by example, BBE, json, xml ================================================ FILE: examples/xml-to-json-conversion/xml_to_json_conversion.out ================================================ $ bal run xml_to_json_conversion.bal {"name":"Anne","address":{"street":"Main","city":"94"},"codes":["4","8"],"id":"AST"} ================================================ FILE: examples/xml-to-record/xml_to_record.bal ================================================ import ballerina/data.xmldata; import ballerina/io; type Book record { int id; string title; string author; }; xml xmlData = xml ` 10024 Clean Code Robert C. Martin `; string xmlStr = string ` 10024 Clean Code Robert C. Martin `; public function main() returns error? { // Convert the XML value to a record type. Book book1 = check xmldata:parseAsType(xmlData); io:println(book1); // Convert the XML string to a record type. Book book2 = check xmldata:parseString(xmlStr); io:println(book2); byte[] xmlByteArr = xmlStr.toBytes(); // Convert the XML byte array to a record type. Book book3 = check xmldata:parseBytes(xmlByteArr); io:println(book3); stream byteBlockStream = new (new ByteBlockGenerator(xmlStr)); // Convert the XML byte block stream to a record type. Book book4 = check xmldata:parseStream(byteBlockStream); io:println(book4); } // Defines a class called `ByteBlockGenerator`, which implements the `next()` method. // This will be invoked when the `next()` method of the stream gets invoked. class ByteBlockGenerator { private int index = 0; private final byte[] byteArr; private final int arraySize; public function init(string data) { self.byteArr = data.toBytes(); self.arraySize = self.byteArr.length(); } public isolated function next() returns record {|byte[] value;|}|error? { if self.index >= self.arraySize { return; } int startIndex = self.index; self.index = startIndex + 4 > self.arraySize ? self.arraySize : startIndex + 3; return {value: self.byteArr.slice(startIndex, self.index)}; } } ================================================ FILE: examples/xml-to-record/xml_to_record.md ================================================ # XML to Record conversion The `data.xmldata` library provides multiple APIs to perform the conversion from XML source, which can be provided as a `string`, `byte array`, `byte block stream`, or `xml`, into a Ballerina record. For more information on the underlying module, see the [`data.xmldata` module](https://lib.ballerina.io/ballerina/data.xmldata/latest/). ::: code xml_to_record.bal ::: ::: out xml_to_record.out ::: ================================================ FILE: examples/xml-to-record/xml_to_record.metatags ================================================ description: This BBE demonstrates how to convert a XML source, which can be provided as a string, byte array, byte block stream, or xml, into a Ballerina record. keywords: ballerina, ballerina by example, BBE, xml, record, stream, byte array ================================================ FILE: examples/xml-to-record/xml_to_record.out ================================================ $ bal run xml_to_record.bal {"id":10024,"title":"Clean Code","author":"Robert C. Martin"} {"id":10024,"title":"Clean Code","author":"Robert C. Martin"} {"id":10024,"title":"Clean Code","author":"Robert C. Martin"} {"id":10024,"title":"Clean Code","author":"Robert C. Martin"} ================================================ FILE: examples/xml-to-record-conversion/xml_to_record_conversion.bal ================================================ import ballerina/data.xmldata; import ballerina/io; // Defines a record type with annotations. @xmldata:Namespace { prefix: "ns", uri: "http://sdf.com" } type Invoice record { int id; Item[] purchased_item; @xmldata:Attribute string status?; }; @xmldata:Namespace { uri: "example.com" } type Item record { string itemCode; int item_count; }; public function main() returns error? { xml data = xml ` 1 223345 1 223300 7 `; // Converts an XML representation to its `record` representation. Invoice output = check xmldata:parseAsType(data); io:println(output); } ================================================ FILE: examples/xml-to-record-conversion/xml_to_record_conversion.md ================================================ # XML to Record conversion The `data.xmldata` library provides an API to perform the conversion from XML to a Ballerina record. For more information on the underlying module, see the [`data.xmldata` module](https://lib.ballerina.io/ballerina/data.xmldata/latest/). ::: code xml_to_record_conversion.bal ::: To run this sample, use the `bal run` command. ::: out xml_to_record_conversion.out ::: ================================================ FILE: examples/xml-to-record-conversion/xml_to_record_conversion.metatags ================================================ description: This BBE demonstrates the conversion from XML to record in Ballerina. keywords: ballerina, ballerina by example, BBE, record, xml ================================================ FILE: examples/xml-to-record-conversion/xml_to_record_conversion.out ================================================ $ bal run xml_to_record_conversion.bal {"id":1,"purchased_item":[{"itemCode":"223345","item_count":1},{"itemCode":"223300","item_count":7}],"status":"paid"} ================================================ FILE: examples/xml-to-record-with-projection/xml_to_record_with_projection.bal ================================================ import ballerina/data.xmldata; import ballerina/io; // Define a closed record type to capture the required elements and attributes from the XML data. type Book record {| string name; string author; |}; xml xmlData = xml ` Clean Code Robert C. Martin 2008 Prentice Hall `; string xmlStr = string ` Clean Code Robert C. Martin 2008 Prentice Hall `; public function main() returns error? { // Based on the expected type, it selectively converts the XML data to the record type. Book book = check xmldata:parseAsType(xmlData); io:println(book); // Based on the expected type, it selectively converts the XML string to the record type. Book book2 = check xmldata:parseString(xmlStr); io:println(book2); } ================================================ FILE: examples/xml-to-record-with-projection/xml_to_record_with_projection.md ================================================ # XML to record conversion with projection The `data.xmldata` library provides multiple APIs to selectively convert elements and attributes from a XML source, which can be provided as a `string`, `byte array`, `byte block stream`, or `xml`, into a Ballerina record. For more information on the underlying module, see the [`data.xmldata` module](https://lib.ballerina.io/ballerina/data.xmldata/latest/). ::: code xml_to_record_with_projection.bal ::: ::: out xml_to_record_with_projection.out ::: ================================================ FILE: examples/xml-to-record-with-projection/xml_to_record_with_projection.metatags ================================================ description: This BBE demonstrates how to selectively convert fields from a XML source, which can be provided as a string, byte array, byte block stream, or xml, into a Ballerina record. keywords: ballerina, ballerina by example, BBE, xml, record, data projection ================================================ FILE: examples/xml-to-record-with-projection/xml_to_record_with_projection.out ================================================ $ bal run xml_to_record_with_projection.bal {"name":"Clean Code","author":"Robert C. Martin"} {"name":"Clean Code","author":"Robert C. Martin"} ================================================ FILE: examples/xmlns-declarations/xmlns_declarations.bal ================================================ import ballerina/io; // The identifier followed by the `as` keyword is the prefix bound to the namespace name. // Here `eg` is bound to `"http://example.com"`. xmlns "http://example.com" as eg; public function main() { // The prefixes to which namespaces declarations are bound to can be used in XML templates. xml x = xml `Hello`; // `xmlns` declarations are also allowed at block level. // The `ex` prefix is also bound to `"http://example.com"`. xmlns "http://example.com" as ex; // The prefixes can also be used in XML navigation. boolean b = x === x.; // Since both `eg` and `ex` bind to the same namespace URL, the following evaluates to `true`. io:println(b); // An XML qualified name `ex:doc` evaluates to the string `{http://example.com}doc` where // `http://example.com` is the namespace URL bound to `ex`. string exdoc = ex:doc; io:println(exdoc); } ================================================ FILE: examples/xmlns-declarations/xmlns_declarations.md ================================================ # XMLNS declarations The `xmlns` declarations are like import declarations, but bind the prefix to a namespace URL rather than a module. The `xmlns` declarations in the Ballerina module provide namespace context for parsing `xml` templates. The Qualified names in Ballerina modules are expanded into `strings` using the `xmlns` declarations in the module. The `xmlns` declarations are also allowed at block level. ::: code xmlns_declarations.bal ::: ::: out xmlns_declarations.out ::: ## Related links - [XML data model](/learn/by-example/xml-data-model/) - [XML namespaces](/learn/by-example/xml-namespaces/) ================================================ FILE: examples/xmlns-declarations/xmlns_declarations.metatags ================================================ description: This BBE demonstrates xmlns declarations in Ballerina. keywords: ballerina, ballerina by example, bbe, xmlns declarations ================================================ FILE: examples/xmlns-declarations/xmlns_declarations.out ================================================ $ bal run xmlns_declarations.bal true {http://example.com}doc ================================================ FILE: examples/xslt-transformation/xslt_transformation.bal ================================================ import ballerina/io; import ballerina/xslt; public function main() returns error? { // Gets an `XML` value, which needs to be transformed. xml sourceXml = getXml(); // Gets an `XSL` style sheet represented in an XML value. xml xsl = getXsl(); // Transforms the `XML` content to another format. // For details, see https://lib.ballerina.io/ballerina/xslt/latest#transform. xml target = check xslt:transform(sourceXml, xsl); io:println("Transformed XML: ", target); } // Returns an `XML` element, which needs to be transformed. function getXml() returns xml { return xml ` Summer of 69 Bryan Adams Canada 1984 Zombie Bad Wolves USA 2018 `; } // Returns an `XSL` style sheet represented by an XML element. function getXsl() returns xml { return xml `

All time favourites

Title Artist
`; } ================================================ FILE: examples/xslt-transformation/xslt_transformation.md ================================================ # XSLT transformation The `xslt` library provides an API to transform XML content to HTML using XSL transformation. For more information on the underlying module, see the [`xslt` module](https://lib.ballerina.io/ballerina/xslt/latest/). ::: code xslt_transformation.bal ::: To run this sample, use the `bal run` command.: ::: out xslt_transformation.out ::: ================================================ FILE: examples/xslt-transformation/xslt_transformation.metatags ================================================ description: This BBE demonstrates how the XML content can be transformed into HTML using a given XSL transformation. keywords: ballerina, ballerina by example, bbe, xslt, xml, html, xsl, transformation ================================================ FILE: examples/xslt-transformation/xslt_transformation.out ================================================ $ bal run xslt_transformation.bal Transformed XML:

All time favourites

TitleArtist
Summer of 69Bryan Adams
ZombieBad Wolves
================================================ FILE: examples/yaml-to-anydata/yaml_to_anydata.bal ================================================ import ballerina/data.yaml; import ballerina/io; type ServerConfig record {| string host; int port; string protocol; |}; final string yamlString = string ` host: "localhost" port: 8080 protocol: "http"`; public function main() returns error? { // Parse the YAML string as a record. ServerConfig serverConfig1 = check yaml:parseString(yamlString); io:println(serverConfig1); byte[] yamlByteArr = yamlString.toBytes(); // Parse the YAML byte array as a record. ServerConfig serverConfig2 = check yaml:parseBytes(yamlByteArr); io:println(serverConfig2); stream byteBlockStream = new (new ByteBlockGenerator(yamlString)); // Parse the YAML byte block stream as a record. ServerConfig serverConfig3 = check yaml:parseStream(byteBlockStream); io:println(serverConfig3); } // Defines a class called `ByteBlockGenerator`, which is stream implementor with a `next()` method. // This `next()` method is called when iterating over a stream created with a `ByteBlockGenerator` value. class ByteBlockGenerator { private int index = 0; private final byte[] byteArr; private final int arraySize; public function init(string data) { self.byteArr = data.toBytes(); self.arraySize = self.byteArr.length(); } public isolated function next() returns record {|byte[] value;|}|error? { if self.index >= self.arraySize { return; } int startIndex = self.index; self.index = startIndex + 4 > self.arraySize ? self.arraySize : startIndex + 3; return {value: self.byteArr.slice(startIndex, self.index)}; } } ================================================ FILE: examples/yaml-to-anydata/yaml_to_anydata.md ================================================ # YAML to anydata conversion The `data.yaml` library provides multiple functions to parse YAML source, in the form of a string, byte array, or byte block stream, as a value that belongs to a subtype of `anydata`. For more information on the underlying module, see the [`data.yaml` module](https://lib.ballerina.io/ballerina/data.yaml/latest/). ::: code yaml_to_anydata.bal ::: ::: out yaml_to_anydata.out ::: ================================================ FILE: examples/yaml-to-anydata/yaml_to_anydata.metatags ================================================ description: This BBE demonstrates how to parse a YAML source, which can be provided as a string, byte array, or byte block stream, as a value that belongs to a subtype of anydata. keywords: ballerina, ballerina by example, BBE, yaml, record, stream, byte array ================================================ FILE: examples/yaml-to-anydata/yaml_to_anydata.out ================================================ $ bal run yaml_to_anydata.bal {"host":"localhost","port":8080,"protocol":"http"} {"host":"localhost","port":8080,"protocol":"http"} {"host":"localhost","port":8080,"protocol":"http"} ================================================ FILE: examples/yaml-to-anydata-with-projection/yaml_to_anydata_with_projection.bal ================================================ import ballerina/data.yaml; import ballerina/io; type ServerConfig record {| string host; int port; int[2] remotePorts; DatabaseConfig database; |}; type DatabaseConfig record {| string dbName; string username; |}; public function main() returns error? { // Similar to content read from a YAML file. string yamlString = string ` host: "localhost" port: 8080 remotePorts: [9000, 9001, 9002, 9003] protocol: "http" database: dbName: "testdb" username: "dbuser" password: "dbpassword"`; // Based on the expected type, this function selectively constructs the record from the YAML string. ServerConfig serverConfig = check yaml:parseString(yamlString); // The `password` field is excluded in the created record value. // Only the first two elements from the source are used to create the `remotePorts` array. io:println(serverConfig); } ================================================ FILE: examples/yaml-to-anydata-with-projection/yaml_to_anydata_with_projection.md ================================================ # YAML to anydata conversion with projection The `data.yaml` library provides multiple APIs to selectively parse elements and attributes from a YAML source, in the form of a string, byte array, or byte block stream, into a Ballerina record. Using projection, users can selectively add fields to records and limit the sizes of arrays. In this example, the `password` attribute is excluded when creating the `DatabaseConfig` record, and only the first two elements from the source are used to create the `remotePorts` array. For more information on the underlying module, see the [`data.yaml` module](https://lib.ballerina.io/ballerina/data.yaml/latest/). ::: code yaml_to_anydata_with_projection.bal ::: ::: out yaml_to_anydata_with_projection.out ::: ================================================ FILE: examples/yaml-to-anydata-with-projection/yaml_to_anydata_with_projection.metatags ================================================ description: This BBE demonstrates how to selectively convert fields from a YAML source, which can be provided as a string, byte array, or byte block stream, into a Ballerina record with projection. keywords: ballerina, ballerina by example, BBE, yaml, record, data projection ================================================ FILE: examples/yaml-to-anydata-with-projection/yaml_to_anydata_with_projection.out ================================================ $ bal run yaml_to_anydata_with_projection.bal {"host":"localhost","port":8080,"remotePorts":[9000,9001],"database":{"dbName":"testdb","username":"dbuser"}} ================================================ FILE: gradle/javaProject.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 plugin: "java" apply plugin: "com.github.spotbugs" apply plugin: "checkstyle" apply plugin: "jacoco" repositories { mavenLocal() maven { url = 'https://maven.wso2.org/nexus/content/repositories/releases/' } maven { url = 'https://maven.wso2.org/nexus/content/groups/wso2-public/' } maven { url = 'https://repo.maven.apache.org/maven2' } } configurations { jacocoRuntime } dependencies { implementation "org.slf4j:slf4j-api:${slf4jVersion}" testImplementation "org.slf4j:slf4j-api:${slf4jVersion}" constraints { implementation "com.fasterxml.jackson.core:jackson-databind:2.11.1" implementation "com.github.jknack:handlebars:4.2.0" implementation "com.google.code.findbugs:jsr305:3.0.2" implementation "info.picocli:picocli:4.0.1" implementation "io.swagger.core.v3:swagger-core:2.1.6" implementation "io.swagger.core.v3:swagger-models:2.1.6" implementation "io.swagger.parser.v3:swagger-parser-v2-converter:2.0.24" implementation "io.swagger.parser.v3:swagger-parser:2.0.24" implementation "javax.ws.rs:javax.ws.rs-api:2.1.1" implementation "org.ballerinalang:ballerina-lang:${ballerinaLangVersion}" implementation "org.ballerinalang:ballerina-parser:${ballerinaLangVersion}" implementation "org.ballerinalang:ballerina-cli:${ballerinaLangVersion}" implementation "org.ballerinalang:formatter-core:${ballerinaLangVersion}" implementation "org.ballerinalang:ballerina-tools-api:${ballerinaLangVersion}" implementation "io.ballerina.stdlib:http-native:${stdlibHttpVersion}" testImplementation "org.testng:testng:${testngVersion}" } jacocoRuntime "org.jacoco:org.jacoco.agent:${jacoco.toolVersion}:runtime" checkstyle "com.puppycrawl.tools:checkstyle:${puppycrawlCheckstyleVersion}" } sourceCompatibility = JavaVersion.VERSION_21 tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } jacoco { toolVersion = "0.8.10" } test { systemProperty "ballerina.home", "$buildDir" systemProperty "org.apache.commons.logging.Log", "org.apache.commons.logging.impl.NoOpLog" testLogging { showStackTraces = true showStandardStreams = true events "failed" exceptionFormat "full" } jacoco { enabled = true destinationFile = file("$buildDir/coverage-reports/jacoco.exec") includeNoLocationClasses = true excludes = ['jdk.internal.*'] } } checkstyle { toolVersion '10.12.1' configFile rootProject.file("config/checkstyle/build/checkstyle.xml") configProperties = ["suppressionFile": rootProject.file("config/checkstyle/build/suppressions.xml")] checkstyleTest.enabled = true checkstyleMain.enabled = false } spotbugsMain { spotbugsMain.enabled = false ignoreFailures = true effort = "max" reportLevel = "low" reportsDir = file("$project.buildDir/reports/spotbugs") def excludeFile = file("spotbugs-exclude.xml") if (excludeFile.exists()) { it.excludeFilter = excludeFile } reports { text.enabled = false } } spotbugsTest { it.enabled = false } jacocoTestReport { reports { xml.required = true } } checkstyleMain.dependsOn(":config:checkstyle:downloadMultipleFiles") checkstyleTest.dependsOn(":config:checkstyle:downloadMultipleFiles") ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists ================================================ FILE: gradle.properties ================================================ org.gradle.caching=true group=org.ballerinalang # During the release workflow, the following will get bumped automatically version=2201.14.0-SNAPSHOT codeName=swan-lake ballerinaLangVersion=2201.14.0-20260513-003900-6983b294f4c ballerinaJreVersion=3.0.0 dependencyJREVersion=jdk-21.0.5+11-jre specVersion=2024R1 slf4jVersion=1.7.30 balstdlibBranch=master devIdpUrl=https://dev.api.asgardeo.io/oauth2/token/.well-known/openid-configuration # Stdlib Level 01 stdlibIoVersion=1.8.0 stdlibJavaArraysVersion=1.6.0 stdlibTimeVersion=2.8.0 stdlibUrlVersion=2.6.1 stdlibXmldataVersion=2.9.1 stdlibMathVectorVersion=1.2.0 observeVersion=1.7.0 # Stdlib Level 02 stdlibAvroVersion=1.2.1 stdlibConstraintVersion=1.7.0 stdlibCryptoVersion=2.9.2 stdlibDataXmldataVersion=1.5.2 stdlibLogVersion=2.17.0 stdlibOsVersion=1.10.1 stdlibPersistVersion=1.6.0 stdlibProtobufVersion=1.8.0 stdlibRandomVersion=1.7.0 stdlibTaskVersion=2.11.0 stdlibXsltVersion=2.9.1 observeInternalVersion=1.7.0 # Stdlib Level 03 stdlibCacheVersion=3.10.0 stdlibFileVersion=1.12.0 stdlibFtpVersion=2.18.0 stdlibLdapVersion=1.3.0 stdlibMimeVersion=2.12.0 stdlibTcpVersion=1.13.2 stdlibUdpVersion=1.13.2 stdlibUuidVersion=1.10.0 # Stdlib Level 04 stdlibAuthVersion=2.14.0 stdlibDataCsvVersion=0.8.1 stdlibDataJsonDataVersion=1.1.3 stdlibDataYamlVersion=0.8.0 stdlibEdiVersion=1.5.3 stdlibEmailVersion=2.13.0 stdlibJwtVersion=2.15.1 stdlibMqttVersion=1.4.0 stdlibOAuth2Version=2.15.0 stdlibTomlVersion=0.8.0 stdlibYamlVersion=0.8.0 # Stdlib Level 05 stdlibHttpVersion=2.16.2 stdlibMessagingVersion=1.0.0 # Stdlib Level 06 stdlibGrpcVersion=1.14.2 stdlibSoapVersion=2.3.0 stdlibTransactionVersion=1.12.0 stdlibWebsocketVersion=2.15.1 stdlibWebsubVersion=2.15.0 stdlibWebsubhubVersion=1.16.0 # Stdlib Level 07 stdlibGraphqlVersion=1.17.0 stdlibSqlVersion=1.17.1 # Stdlib Level 08 stdlibMcpVersion=1.0.3 # Stdlib Level 09 stdlibAiVersion=1.11.1 # Stdlib Level 10 stdlibAiNpVersion=0.5.1 # OpenAPI Module openapiModuleVersion=2.4.0 # Dev Tools devToolsVersion=2.6.1 ballerinaCommandVersion=1.5.1 # API Doc UI docUiApi=https://api.dev-central.ballerina.io/2.0/docs/doc-ui # CLI Tools openapiToolVersion=2.4.0 persistToolVersion=1.7.0 graphqlToolVersion=0.14.0 protocToolVersion=1.0.0 asyncapiToolVersion=0.12.0 c2cVersion=4.0.0-20250915-120000-41c6c17 installerVersion=500db315-7e0b-4ae3-8780-bdb358f81cfd ================================================ FILE: gradlew ================================================ #!/bin/sh # # Copyright © 2015-2021 the original 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 # # https://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 POSIX generated by Gradle. # # Important for running: # # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is # noncompliant, but you have some other compliant shell such as ksh or # bash, then to run this script, type that shell name before the whole # command line, like: # # ksh Gradle # # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: # * functions; # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», # «${var#prefix}», «${var%suffix}», and «$( cmd )»; # * compound commands having a testable exit status, especially «case»; # * various built-in commands including «command», «set», and «ulimit». # # Important for patching: # # (2) This script targets any POSIX shell, so it avoids extensions provided # by Bash, Ksh, etc; in particular arrays are avoided. # # The "traditional" practice of packing multiple parameters into a # space-separated string is a well documented source of bugs and security # problems, so this is (mostly) avoided, by progressively accumulating # options in "$@", and eventually passing that to Java. # # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; # see the in-line comments for details. # # There are tweaks for specific operating systems such as AIX, CygWin, # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. # ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link app_path=$0 # Need this for daisy-chained symlinks. while APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path [ -h "$app_path" ] do ls=$( ls -ld "$app_path" ) link=${ls#*' -> '} case $link in #( /*) app_path=$link ;; #( *) app_path=$APP_HOME$link ;; esac done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum warn () { echo "$*" } >&2 die () { echo echo "$*" echo exit 1 } >&2 # 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 ;; #( MSYS* | 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 if ! command -v java >/dev/null 2>&1 then 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 fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi # Collect all arguments for the java command, stacking in reverse order: # * args from the command line # * the main class name # * -classpath # * -D...appname settings # * --module-path (only if needed) # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) # Now convert the arguments - kludge to limit ourselves to /bin/sh for arg do if case $arg in #( -*) false ;; # don't mess with options #( /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath [ -e "$t" ] ;; #( *) false ;; esac then arg=$( cygpath --path --ignore --mixed "$arg" ) fi # Roll the args list around exactly as many times as the number of # args, so each arg winds up back in the position where it started, but # possibly modified. # # NB: a `for` loop captures its iteration list before it begins, so # changing the positional parameters here affects neither the number of # iterations, nor the values presented in `arg`. shift # remove old arg set -- "$@" "$arg" # push replacement arg done fi # 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"' # Collect all arguments for the java command; # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # shell script including quotes and variable substitutions, so put them in # double quotes to make sure that they get re-expanded; and # * put everything else in single quotes, so that it's not re-expanded. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ org.gradle.wrapper.GradleWrapperMain \ "$@" # Stop when "xargs" is not available. if ! command -v xargs >/dev/null 2>&1 then die "xargs is not available" fi # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. # # In Bash we could simply go: # # readarray ARGS < <( xargs -n1 <<<"$var" ) && # set -- "${ARGS[@]}" "$@" # # but POSIX shell has neither arrays nor command substitution, so instead we # post-process each arg (as a line of input to sed) to backslash-escape any # character that might be a shell metacharacter, then use eval to reverse # that process (while maintaining the separation between arguments), and wrap # the whole thing up as a single "set" statement. # # This will of course break if any of these variables contains a newline or # an unmatched quote. # eval "set -- $( printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | xargs -n1 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | tr '\n' ' ' )" '"$@"' exec "$JAVACMD" "$@" ================================================ FILE: 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 https://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=. @rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Resolve any "." and ".." in APP_HOME to make it shorter. for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi @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% equ 0 goto execute 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 execute 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 :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 %* :end @rem End local scope for the variables with windows NT shell if %ERRORLEVEL% equ 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! set EXIT_CODE=%ERRORLEVEL% if %EXIT_CODE% equ 0 set EXIT_CODE=1 if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: installers/linux-deb/README.md ================================================ # ballerina-linux-deb-installers ================================================ FILE: installers/linux-deb/build-ballerina-linux-deb-x64.sh ================================================ #!/bin/bash function printUsage() { echo "Usage:" echo "$0 [options]" echo "options:" echo " -v (--version)" echo " version of the ballerina distribution" echo " -p (--path)" echo " path of the ballerina distributions" echo " -a (--arch)" echo " architecture of the ballerina distribution" echo " If not specified : x86" echo " arm : aarch64/arm64" echo " -d (--dist)" echo " ballerina distribution type either of the followings" echo " If not specified both distributions will be built" echo " 1. ballerina" echo " 2. ballerina-runtime" echo "eg: $0 -v 1.0.0 -p /home/username/Packs" echo "eg: $0 -v 1.0.0 -p /home/username/Packs -d ballerina" } BUILD_ALL_DISTRIBUTIONS=false POSITIONAL=() while [[ $# -gt 0 ]] do key="$1" case ${key} in -v|--version) BALLERINA_VERSION="$2" shift # past argument shift # past value ;; -p|--path) DIST_PATH="$2" shift # past argument shift # past value ;; -a|--arch) ARCH="$2" shift # past argument shift # past value ;; -d|--dist) DISTRIBUTION="$2" shift # past argument shift # past value ;; *) # unknown option POSITIONAL+=("$1") # save it in an array for later shift # past argument ;; esac done if [ -z "$BALLERINA_VERSION" ]; then echo "Please enter the version of the ballerina pack" printUsage exit 1 fi if [ -z "$DIST_PATH" ]; then echo "Please enter the path of the ballerina packs" printUsage exit 1 fi if [ -z "$DISTRIBUTION" ]; then BUILD_ALL_DISTRIBUTIONS=true fi if [ -z "$ARCH" ]; then BALLERINA_PLATFORM=ballerina-${BALLERINA_VERSION}-linux else if [ "$ARCH" = "arm" ]; then BALLERINA_PLATFORM=ballerina-${BALLERINA_VERSION}-linux-arm else echo "Please enter a valid architecture for the ballerina pack" printUsage exit 1 fi fi BALLERINA_DISTRIBUTION_LOCATION=${DIST_PATH} BALLERINA_INSTALL_DIRECTORY=ballerina-${BALLERINA_VERSION} echo "Build started at" $(date +"%Y-%m-%d %H:%M:%S") function deleteTargetDirectory() { echo "Deleting target directory" rm -rf target } function extractPack() { echo "Extracting the ballerina distribution, " $1 rm -rf target/original mkdir -p target/original unzip $1 -d target/original > /dev/null 2>&1 mv target/original/$2 target/original/${BALLERINA_INSTALL_DIRECTORY} } function createPackInstallationDirectory() { rm -rf target/${BALLERINA_INSTALL_DIRECTORY}/usr/lib/ballerina mkdir -p target/${BALLERINA_INSTALL_DIRECTORY}/usr/lib/ballerina chmod -R 755 target/${BALLERINA_INSTALL_DIRECTORY}/usr/lib/ballerina mv target/original/${BALLERINA_INSTALL_DIRECTORY}/* target/${BALLERINA_INSTALL_DIRECTORY}/usr/lib/ballerina rm -rf target/${BALLERINA_INSTALL_DIRECTORY}/usr/share/doc/${BALLERINA_INSTALL_DIRECTORY} mkdir -p target/${BALLERINA_INSTALL_DIRECTORY}/usr/share/doc/${BALLERINA_INSTALL_DIRECTORY} chmod -R 755 target/${BALLERINA_INSTALL_DIRECTORY}/usr/share/doc/${BALLERINA_INSTALL_DIRECTORY} } function copyDebianDirectory() { if [ "$ARCH" = "arm" ]; then cp -R resources/arm/DEBIAN target/${BALLERINA_INSTALL_DIRECTORY}/DEBIAN else cp -R resources/amd/DEBIAN target/${BALLERINA_INSTALL_DIRECTORY}/DEBIAN fi sed -i -e 's/__BALLERINA_VERSION__/'${BALLERINA_VERSION}'/g' target/${BALLERINA_INSTALL_DIRECTORY}/DEBIAN/postinst sed -i -e 's/__BALLERINA_VERSION__/'${BALLERINA_VERSION}'/g' target/${BALLERINA_INSTALL_DIRECTORY}/DEBIAN/postrm sed -i -e 's/__BALLERINA_VERSION__/'${BALLERINA_VERSION}'/g' target/${BALLERINA_INSTALL_DIRECTORY}/DEBIAN/control chmod 755 target/${BALLERINA_INSTALL_DIRECTORY}/DEBIAN/postrm chmod 755 target/${BALLERINA_INSTALL_DIRECTORY}/DEBIAN/postinst } function createBallerinaPlatform() { echo "Creating ballerina platform installer" extractPack "$BALLERINA_DISTRIBUTION_LOCATION/$BALLERINA_PLATFORM.zip" ${BALLERINA_PLATFORM} createPackInstallationDirectory copyDebianDirectory mv target/${BALLERINA_INSTALL_DIRECTORY} target/${BALLERINA_PLATFORM}-x64 fakeroot dpkg-deb --build target/${BALLERINA_PLATFORM}-x64 } deleteTargetDirectory if [ "$BUILD_ALL_DISTRIBUTIONS" == "true" ]; then echo "Creating all distributions" createBallerinaPlatform else if [ "$DISTRIBUTION" == "ballerina" ]; then echo "Creating Ballerina Platform" createBallerinaPlatform else echo "Error" fi fi echo "Build completed at" $(date +"%Y-%m-%d %H:%M:%S") ================================================ FILE: installers/linux-deb/resources/amd/DEBIAN/control ================================================ Package: ballerina-__BALLERINA_VERSION__ Version: 2-__BALLERINA_VERSION__ Maintainer: Ballerina Architecture: amd64 Description: Ballerina Ballerina is a general purpose, concurrent and strongly typed programming language with both textual and graphical syntaxes, optimized for integration. Installed-Size: 331000 ================================================ FILE: installers/linux-deb/resources/amd/DEBIAN/postinst ================================================ rm -f /usr/lib/ballerina/bin/ballerina rm -f /usr/bin/bal ln -sf /usr/lib/ballerina/bin/bal /usr/bin/bal echo "export BALLERINA_HOME=/usr/lib/bal" >> /etc/profile.d/wso2.sh if [ "$(basename -- "$SHELL")" = "bash" ]; then bal completion bash > /usr/share/bash-completion/completions/bal chmod 766 /usr/share/bash-completion/completions/bal elif [ "$(basename -- "$SHELL")" = "zsh" ]; then if [ ! -d ~/.ballerina ]; then mkdir –m766 ~/.ballerina fi mkdir -p –m766 ~/.ballerina/completion echo 'fpath=(~/.ballerina/completion $fpath)' >> ~/.zshrc echo 'autoload -U compinit && compinit' >> ~/.zshrc \cp /usr/lib/ballerina/scripts/_bal ~/.ballerina/completion/ chmod 766 ~/.ballerina/completion/_bal fi ================================================ FILE: installers/linux-deb/resources/amd/DEBIAN/postrm ================================================ sudo rm /usr/bin/bal sed -i.bak '/BALLERINA_HOME/d' /etc/profile.d/wso2.sh ================================================ FILE: installers/linux-deb/resources/arm/DEBIAN/control ================================================ Package: ballerina-__BALLERINA_VERSION__ Version: 2-__BALLERINA_VERSION__ Maintainer: Ballerina Architecture: arm64 Description: Ballerina Ballerina is a general purpose, concurrent and strongly typed programming language with both textual and graphical syntaxes, optimized for integration. Installed-Size: 331000 ================================================ FILE: installers/linux-deb/resources/arm/DEBIAN/postinst ================================================ rm -f /usr/lib/ballerina/bin/ballerina rm -f /usr/bin/bal ln -sf /usr/lib/ballerina/bin/bal /usr/bin/bal echo "export BALLERINA_HOME=/usr/lib/bal" >> /etc/profile.d/wso2.sh if [ "$(basename -- "$SHELL")" = "bash" ]; then bal completion bash > /usr/share/bash-completion/completions/bal chmod 766 /usr/share/bash-completion/completions/bal elif [ "$(basename -- "$SHELL")" = "zsh" ]; then if [ ! -d ~/.ballerina ]; then mkdir –m766 ~/.ballerina fi mkdir -p –m766 ~/.ballerina/completion echo 'fpath=(~/.ballerina/completion $fpath)' >> ~/.zshrc echo 'autoload -U compinit && compinit' >> ~/.zshrc \cp /usr/lib/ballerina/scripts/_bal ~/.ballerina/completion/ chmod 766 ~/.ballerina/completion/_bal fi ================================================ FILE: installers/linux-deb/resources/arm/DEBIAN/postrm ================================================ sudo rm /usr/bin/bal sed -i.bak '/BALLERINA_HOME/d' /etc/profile.d/wso2.sh ================================================ FILE: installers/linux-rpm/build-ballerina-linux-rpm-x64.sh ================================================ #!/bin/bash function printUsage() { echo "Usage:" echo "$0 [options]" echo "options:" echo " -v (--version)" echo " version of the ballerina distribution" echo " -p (--path)" echo " path of the ballerina distributions" echo " -d (--dist)" echo " ballerina distribution type either of the followings" echo " If not specified both distributions will be built" echo " 1. ballerina" echo " 2. ballerina-runtime" echo "eg: $0 -v 1.0.0 -p /home/username/Packs" echo "eg: $0 -v 1.0.0 -p /home/username/Packs -d ballerina" } BUILD_ALL_DISTRIBUTIONS=false POSITIONAL=() while [[ $# -gt 0 ]] do key="$1" case ${key} in -v|--version) BALLERINA_VERSION="$2" shift # past argument shift # past value ;; -p|--path) DIST_PATH="$2" shift # past argument shift # past value ;; -d|--dist) DISTRIBUTION="$2" shift # past argument shift # past value ;; *) # unknown option POSITIONAL+=("$1") # save it in an array for later shift # past argument ;; esac done if [ -z "$BALLERINA_VERSION" ]; then echo "Please enter the version of the ballerina pack" printUsage exit 1 fi if [ -z "$DIST_PATH" ]; then echo "Please enter the path of the ballerina packs" printUsage exit 1 fi if [ -z "$DISTRIBUTION" ]; then BUILD_ALL_DISTRIBUTIONS=true fi BALLERINA_DISTRIBUTION_LOCATION=${DIST_PATH} BALLERINA_PLATFORM=ballerina-${BALLERINA_VERSION}-linux BALLERINA_INSTALL_DIRECTORY=ballerina-${BALLERINA_VERSION} PLATFORM_SPEC_FILE="ballerina-tools.spec" SPEC_FILES_LOCATION="rpmbuild/SPECS/" PLATFORM_SPEC_FILE_LOC=${SPEC_FILES_LOCATION}/${PLATFORM_SPEC_FILE} RPM_BALLERINA_VERSION=$(echo "${BALLERINA_VERSION//-/.}") echo "Build started at" $(date +"%Y-%m-%d %H:%M:%S") function extractPack() { echo "Extracting the ballerina distribution, " $1 rm -rf rpmbuild/SOURCES mkdir -p rpmbuild/SOURCES unzip $1 -d rpmbuild/SOURCES/ > /dev/null 2>&1 } # Set variables in SPEC file # Globals: # BALLERINA_VERSION # RPM_BALLERINA_VERSION # PLATFORM_SPEC_FILE # Arguments: # Returns: # None function setupVersion_platform() { sed -i "/Version:/c\Version: ${RPM_BALLERINA_VERSION}" ${PLATFORM_SPEC_FILE_LOC} sed -i "/%define _ballerina_version/c\%define _ballerina_version ${BALLERINA_VERSION}" ${PLATFORM_SPEC_FILE_LOC} sed -i "/%define _ballerina_tools_dir/c\%define _ballerina_tools_dir ${BALLERINA_PLATFORM}" ${PLATFORM_SPEC_FILE_LOC} sed -i "s/export BALLERINA_HOME=/export BALLERINA_HOME=\/usr\/lib64\/ballerina\/" ${PLATFORM_SPEC_FILE_LOC} sed -i "s?SED_BALLERINA_HOME?/usr/lib64/ballerina/ballerina-${BALLERINA_VERSION}?" ${PLATFORM_SPEC_FILE_LOC} } # Create Ballerina Platform RPM # Globals: # BALLERINA_DISTRIBUTION_LOCATION # BALLERINA_PLATFORM # PLATFORM_SPEC_FILE # Arguments: # Returns: # None function createBallerinaPlatform() { echo "Creating ballerina platform installer" extractPack "$BALLERINA_DISTRIBUTION_LOCATION/$BALLERINA_PLATFORM.zip" [ -f ${PLATFORM_SPEC_FILE_LOC} ] && rm -f ${PLATFORM_SPEC_FILE_LOC} cp resources/${PLATFORM_SPEC_FILE} ${SPEC_FILES_LOCATION} setupVersion_platform rpmbuild -bb --define "_topdir $(pwd)/rpmbuild" ${PLATFORM_SPEC_FILE_LOC} } if [ "$BUILD_ALL_DISTRIBUTIONS" == "true" ]; then echo "Creating all distributions" createBallerinaPlatform else if [ "$DISTRIBUTION" == "ballerina" ]; then echo "Creating Ballerina Platform" createBallerinaPlatform else echo "Error" fi fi echo "Build completed at" $(date +"%Y-%m-%d %H:%M:%S") ================================================ FILE: installers/linux-rpm/resources/ballerina-runtime.spec ================================================ %define _ballerina_name ballerina %define _ballerina_version %define _ballerina_tools_dir Name: ballerina-runtime Version: Release: 1 Summary: Ballerina is a general purpose, concurrent and strongly typed programming language with both textual and graphical syntaxes, optimized for integration. License: Apache license 2.0 URL: https://ballerina.io/ # Disable Automatic Dependencies AutoReqProv: no # Override RPM file name %define _rpmfilename %%{ARCH}/ballerina-%{_ballerina_version}-runtime-linux-x64.rpm # Disable Jar repacking %define __jar_repack %{nil} %description Ballerina allows you to code with a statically-typed, interaction-centric programming language where microservices, APIs, and streams are first-class constructs. You can use your preferred IDE and CI/CD tools. Discover, consume, and share packages that integrate endpoints with Ballerina Central. Build binaries, containers, and Kubernetes artifacts and deploy as chaos-ready services on cloud and serverless infrastructure. Integrate distributed endpoints with simple syntax for resiliency, circuit breakers, transactions, and events. %pre rm -f /usr/bin/bal > /dev/null 2>&1 %prep rm -rf %{_topdir}/BUILD/* cp -r %{_topdir}/SOURCES/%{_ballerina_tools_dir}/* %{_topdir}/BUILD/ %build %install rm -rf $RPM_BUILD_ROOT install -d %{buildroot}%{_libdir}/ballerina/%{_ballerina_name}-runtime-%{_ballerina_version} cp -r bin bre lib src %{buildroot}%{_libdir}/ballerina/%{_ballerina_name}-runtime-%{_ballerina_version}/ %post ln -sf %{_libdir}/ballerina/%{_ballerina_name}-runtime-%{_ballerina_version}/bin/ballerina /usr/bin/%{_ballerina_name} echo 'export BALLERINA_HOME=' >> /etc/profile.d/wso2.sh chmod 0755 /etc/profile.d/wso2.sh %postun sed -i.bak '\:SED_BALLERINA_HOME:d' /etc/profile.d/wso2.sh if [ "$(readlink /usr/bin/bal)" = "%{_libdir}/ballerina/ballerina-runtime-%{_ballerina_version}/bin/ballerina" ] then rm -f /usr/bin/ballerina fi %clean rm -rf %{_topdir}/BUILD/* rm -rf %{buildroot} bal -v %files %{_libdir}/ballerina/%{_ballerina_name}-runtime-%{_ballerina_version} %doc COPYRIGHT LICENSE README.md ================================================ FILE: installers/linux-rpm/resources/ballerina-tools.spec ================================================ %define _ballerina_name bal %define _ballerina_version %define _ballerina_tools_dir Name: ballerina Version: Release: 1 Summary: Ballerina is a general purpose, concurrent and strongly typed programming language with both textual and graphical syntaxes, optimized for integration. License: Apache license 2.0 URL: https://ballerina.io/ # Disable Automatic Dependencies AutoReqProv: no # Override RPM file name %define _rpmfilename %%{ARCH}/ballerina-%{_ballerina_version}-linux-x64.rpm # Disable Jar repacking %define __jar_repack %{nil} %description Ballerina allows you to code with a statically-typed, interaction-centric programming language where microservices, APIs, and streams are first-class constructs. You can use your preferred IDE and CI/CD tools. Discover, consume, and share packages that integrate endpoints with Ballerina Central. Build binaries, containers, and Kubernetes artifacts and deploy as chaos-ready services on cloud and serverless infrastructure. Integrate distributed endpoints with simple syntax for resiliency, circuit breakers, transactions, and events. %pre rm -f /usr/bin/bal > /dev/null 2>&1 %prep rm -rf %{_topdir}/BUILD/* cp -r %{_topdir}/SOURCES/%{_ballerina_tools_dir}/* %{_topdir}/BUILD/ %install rm -rf $RPM_BUILD_ROOT install -d %{buildroot}%{_libdir}/ballerina/ cp -r ./* %{buildroot}%{_libdir}/ballerina/ %post rm -f %{_libdir}/ballerina/bin/ballerina rm -f /usr/bin/bal ln -sf %{_libdir}/ballerina/bin/bal /usr/bin/%{_ballerina_name} echo 'export BALLERINA_HOME=' >> /etc/profile.d/wso2.sh chmod 0755 /etc/profile.d/wso2.sh if [ "$(basename -- "$SHELL")" = "bash" ]; then bal completion bash > /usr/share/bash-completion/completions/bal chmod 766 /usr/share/bash-completion/completions/bal elif [ "$(basename -- "$SHELL")" = "zsh" ]; then if [ ! -d ~/.ballerina ]; then mkdir –m766 ~/.ballerina fi mkdir -p –m766 ~/.ballerina/completion echo 'fpath=(~/.ballerina/completion $fpath)' >> ~/.zshrc echo 'autoload -U compinit && compinit' >> ~/.zshrc \cp %{_libdir}/ballerina/scripts/_bal ~/.ballerina/completion/ chmod 766 ~/.ballerina/completion/_bal fi %postun sed -i.bak '\:SED_BALLERINA_HOME:d' /etc/profile.d/wso2.sh if [ "$(readlink /usr/bin/ballerina)" = "%{_libdir}/ballerina/bin/ballerina" ] then rm -f /usr/bin/ballerina fi %clean rm -rf %{_topdir}/BUILD/* rm -rf %{buildroot} bal -v %files %{_libdir}/ballerina/ ================================================ FILE: installers/linux-rpm/rpmbuild/BUILDROOT/.gitkeep ================================================ ================================================ FILE: installers/linux-rpm/rpmbuild/RPMS/.gitkeep ================================================ ================================================ FILE: installers/linux-rpm/rpmbuild/SOURCES/.gitkeep ================================================ ================================================ FILE: installers/linux-rpm/rpmbuild/SPECS/.gitkeep ================================================ ================================================ FILE: installers/linux-rpm/rpmbuild/SRPMS/.gitkeep ================================================ ================================================ FILE: installers/mac/build-ballerina-macos-x64.sh ================================================ #!/bin/bash function printUsage() { echo "Usage:" echo "$0 [options]" echo "options:" echo " -v (--version)" echo " version of the ballerina distribution" echo " -p (--path)" echo " path of the ballerina distributions" echo " -a (--arch)" echo " architecture of the ballerina distribution" echo " If not specified : x86" echo " arm : aarch64/arm64" echo " -d (--dist)" echo " ballerina distribution type either of the followings" echo " If not specified both distributions will be built" echo " 1. ballerina" echo " 2. ballerina-runtime" echo "eg: $0 -v 1.0.0 -p /home/username/Packs" echo "eg: $0 -v 1.0.0 -p /home/username/Packs -d ballerina" } BUILD_ALL_DISTRIBUTIONS=false POSITIONAL=() while [[ $# -gt 0 ]] do key="$1" case ${key} in -v|--version) BALLERINA_VERSION="$2" shift # past argument shift # past value ;; -p|--path) DIST_PATH="$2" shift # past argument shift # past value ;; -a|--arch) ARCH="$2" shift # past argument shift # past value ;; -d|--dist) DISTRIBUTION="$2" shift # past argument shift # past value ;; *) # unknown option POSITIONAL+=("$1") # save it in an array for later shift # past argument ;; esac done if [ -z "$BALLERINA_VERSION" ]; then echo "Please enter the version of the ballerina pack" printUsage exit 1 fi if [ -z "$DIST_PATH" ]; then echo "Please enter the path of the ballerina packs" printUsage exit 1 fi if [ -z "$DISTRIBUTION" ]; then BUILD_ALL_DISTRIBUTIONS=true fi if [ -z "$ARCH" ]; then BALLERINA_PLATFORM=ballerina-${BALLERINA_VERSION}-macos else if [ "$ARCH" = "arm" ]; then BALLERINA_PLATFORM=ballerina-${BALLERINA_VERSION}-macos-arm else echo "Please enter a valid architecture for the ballerina pack" printUsage exit 1 fi fi BALLERINA_DISTRIBUTION_LOCATION=${DIST_PATH} BALLERINA_INSTALL_DIRECTORY=ballerina-${BALLERINA_VERSION} BALLERINA_DIST_VERSION="$(cut -d'-' -f1 <<<${BALLERINA_VERSION})" echo "Build started at" $(date +"%Y-%m-%d %H:%M:%S") function deleteTargetDirectory() { echo "Deleting target directory" rm -rf target } function extractPack() { echo "Extracting the ballerina distribution, " $1 rm -rf target/original mkdir -p target/original unzip $1 -d target/original > /dev/null 2>&1 mv target/original/$2 target/original/${BALLERINA_INSTALL_DIRECTORY} } function createPackInstallationDirectory() { rm -rf target/darwin cp -r darwin target/darwin sed -i -e 's/__BALLERINA_VERSION__/'${BALLERINA_DIST_VERSION}'/g' target/darwin/scripts/postinstall chmod -R 755 target/darwin/scripts/postinstall sed -i -e 's/__BALLERINA_VERSION__/'${BALLERINA_VERSION}'/g' target/darwin/Distribution chmod -R 755 target/darwin/Distribution rm -rf target/darwinpkg mkdir -p target/darwinpkg chmod -R 755 target/darwinpkg mkdir -p target/darwinpkg/etc/paths.d chmod -R 755 target/darwinpkg/etc/paths.d echo "/Library/Ballerina/bin" >> target/darwinpkg/etc/paths.d/bal chmod -R 644 target/darwinpkg/etc/paths.d/bal mkdir -p target/darwinpkg/Library/Ballerina chmod -R 755 target/darwinpkg/Library/Ballerina mv target/original/${BALLERINA_INSTALL_DIRECTORY}/* target/darwinpkg/Library/Ballerina/ rm -rf target/package mkdir -p target/package chmod -R 755 target/package mkdir -p target/pkg chmod -R 755 target/pkg } function buildPackage() { pkgbuild --identifier org.ballerina.${BALLERINA_VERSION} \ --version ${BALLERINA_VERSION} \ --scripts target/darwin/scripts \ --root target/darwinpkg \ target/package/ballerina.pkg } function buildProduct() { productbuild --distribution target/darwin/Distribution \ --resources target/darwin/Resources \ --package-path target/package \ target/pkg/$1 > /dev/null 2>&1 } function createBallerinaPlatform() { echo "Creating ballerina platform installer" extractPack "$BALLERINA_DISTRIBUTION_LOCATION/$BALLERINA_PLATFORM.zip" ${BALLERINA_PLATFORM} createPackInstallationDirectory true buildPackage buildProduct ${BALLERINA_PLATFORM}-x64.pkg } deleteTargetDirectory if [ "$BUILD_ALL_DISTRIBUTIONS" == "true" ]; then echo "Creating all distributions" # createBallerinaRuntime createBallerinaPlatform else if [ "$DISTRIBUTION" == "ballerina" ]; then echo "Creating Ballerina Platform" createBallerinaPlatform # elif [ "$DISTRIBUTION" == "ballerina-runtime" ]; then # echo "Creating Ballerina Runtime" # createBallerinaRuntime else echo "Error" fi fi echo "Build completed at" $(date +"%Y-%m-%d %H:%M:%S") ================================================ FILE: installers/mac/darwin/Distribution ================================================ Ballerina ballerina.pkg ================================================ FILE: installers/mac/darwin/app.plist ================================================ BundleIsRelocatable RootRelativeBundlePath Applications/Ballerina Composer.app ================================================ FILE: installers/mac/darwin/scripts/postinstall ================================================ #!/bin/bash BALLERINAROOT=/Library/Ballerina echo "Fixing permissions" cd $BALLERINAROOT find distributions/ballerina-__BALLERINA_VERSION__ -exec chmod ugo+r \{\} \; find bin -exec chmod ugo+rx \{\} \; find dependencies -type d -exec chmod ugo+rx \{\} \; find lib -exec chmod -R ugo+r \{\} \; chmod o-w . rm -f /Library/Ballerina/bin/ballerina rm -f /etc/paths.d/ballerina ================================================ FILE: installers/windows/build-ballerina-windows-x64.bat ================================================ @echo off set BALPOS=windows set ICONDIST=resources\icons :argumentLoop IF NOT "%1"=="" ( IF "%1"=="--dist" ( SET DIST=%2 SHIFT ) IF "%1"=="--version" ( SET BALLERINA_VERSION=%2 SHIFT ) IF "%1"=="--path" ( SET DISTLOC=%2 SHIFT ) SHIFT goto argumentLoop ) IF "%DIST%"=="" ( set DIST=all ) IF "%DISTLOC%"=="" ( set DISTLOC=resources\dist ) IF NOT "%DIST%"=="all" IF NOT "%DIST%"=="ballerina" IF NOT "%DIST%"=="ballerina-runtime" ( echo The syntax of the command is incorrect. Possible arguments for dist - all, ballerina, ballerina-runtime. echo Ex: --dist ballerina goto EOF ) IF "%BALLERINA_VERSION%"=="" ( echo The syntax of the command is incorrect. Missing argument version. goto EOF ) for /f %%x in ('wmic path win32_utctime get /format:list ^| findstr "="') do set %%x set UTC_TIME=%Year%-%Month%-%Day% %Hour%:%Minute%:%Second% UTC rmdir ballerina-%BALLERINA_VERSION%-windows /s /q >nul 2>&1 rmdir target /s /q >nul 2>&1 for /f %%i in ('guid') do set UPGRADECODE=%%i echo Upgrade Code - %UPGRADECODE% IF "%DIST%"=="all" ( call :createballerinaWin64Installer ) ELSE ( call :create%DIST%Win64Installer ) goto EOF :createballerinaWin64Installer set BALZIP=%DISTLOC%\ballerina-%BALLERINA_VERSION%-windows.zip set BALDIST=ballerina-%BALLERINA_VERSION%-windows set BALPARCH=x64 set INSTALLERPARCH=amd64 set MSI=ballerina-%BALLERINA_VERSION%-%BALPOS%-%BALPARCH%.msi set INSTALLERNAME="Ballerina %BALLERINA_VERSION%" call :createInstaller goto EOF :createballerinaWin586Installer set BALZIP=%DISTLOC%\ballerina-%BALLERINA_VERSION%-windows.zip set BALDIST=ballerina-%BALLERINA_VERSION%-windows set BALPARCH=i586 set INSTALLERPARCH=386 set MSI=ballerina-%BALLERINA_VERSION%-%BALPOS%-%BALPARCH%.msi set INSTALLERNAME="Ballerina %BALLERINA_VERSION%" call :createInstaller goto EOF :createInstaller rmdir target\installer-resources /s /q >nul 2>&1 powershell.exe -nologo -noprofile -command "& { Add-Type -A 'System.IO.Compression.FileSystem'; [IO.Compression.ZipFile]::ExtractToDirectory('%BALZIP%', '.'); }" xcopy %ICONDIST% %BALDIST%\icons /e /i >nul 2>&1 echo %BALDIST% build started at '%UTC_TIME%' for %BALPOS% %BALPARCH% echo Creating the Installer... heat dir %BALDIST% -nologo -v -gg -g1 -srd -sfrag -sreg -cg AppFiles -template fragment -dr INSTALLDIR -var var.SourceDir -out target\installer-resources\AppFiles.wxs candle -nologo -sw -dinstallerName=%INSTALLERNAME% -dbalVersion=%BALLERINA_VERSION% -dWixbalVersion=1.0.0 -dUpgradeCode=%UPGRADECODE% -dArch=%INSTALLERPARCH% -dSourceDir=%BALDIST% -out target\installer-resources\ -ext WixUtilExtension resources\installer.wxs target\installer-resources\AppFiles.wxs light -nologo -dcl:high -sw -ext WixUIExtension -ext WixUtilExtension -loc resources\en-us.wxl target\installer-resources\AppFiles.wixobj target\installer-resources\installer.wixobj -o target\msi\%MSI% rmdir ballerina-%BALLERINA_VERSION%-windows /s /q >nul 2>&1 echo %BALDIST% build completed at '%UTC_TIME%' for %BALPOS% %BALPARCH% echo. goto EOF :EOF ================================================ FILE: installers/windows/resources/en-us.wxl ================================================ Click Change to install Ballerina to a different folder. ================================================ FILE: installers/windows/resources/installer.wxs ================================================ NOT Installed 1 WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID="1" VersionNT >= 500 ================================================ FILE: issue_template.md ================================================ **Description:** **Suggested Labels:** **Suggested Assignees:** **Affected Product Version:** **OS, DB, other environment details and versions:** **Steps to reproduce:** **Related Issues:** ================================================ FILE: language-server-simulator/build.gradle ================================================ description = 'Ballerina Language Server Simulator' apply from: "$rootDir/gradle/javaProject.gradle" ext { distributionDir = "distribution" nbalSourceDir = "nBallerinaSrc" fhirSourceDir = "fhirSrc" shortVersion = "${version}".split("-")[0] } configurations { jBallerinaDistribution ballerinaDistribution } dependencies { implementation "org.slf4j:slf4j-api:${project.slf4jApiVersion}" implementation group: 'org.ballerinalang', name: 'ballerina-lang', version: "${ballerinaLangVersion}" implementation group: 'org.ballerinalang', name: 'ballerina-parser', version: "${ballerinaLangVersion}" implementation group: 'org.ballerinalang', name: 'language-server-core', version: "${ballerinaLangVersion}" implementation group: 'org.ballerinalang', name: 'ballerina-tools-api', version: "${ballerinaLangVersion}" implementation group: 'org.ballerinalang', name: 'language-server-commons', version: "${ballerinaLangVersion}" implementation(group: 'org.eclipse.lsp4j', name:'org.eclipse.lsp4j', version:"${eclipseLsp4jVersion}") { exclude group: 'com.google.guava', module: 'guava' } implementation (group: 'org.eclipse.lsp4j', name:'org.eclipse.lsp4j.jsonrpc', version:"${eclipseLsp4jJsonrpcVersion}"){ exclude group: 'com.google.guava', module: 'guava' } implementation "org.slf4j:slf4j-jdk14:${slf4jJdk14Version}" implementation "com.google.code.gson:gson:${gsonVersion}" jBallerinaDistribution project(path: ":ballerina", configuration: "jBallerinaDistribution") ballerinaDistribution project(path: ":ballerina", configuration: "ballerinaDistribution") } task unpackBallerinaDistribution(type: Copy) { dependsOn configurations.jBallerinaDistribution dependsOn configurations.ballerinaDistribution def sourceDir = "${buildDir}/${distributionDir}" from zipTree { "${rootDir}/ballerina/build/distributions/ballerina-${version}-swan-lake.zip" } new File("${sourceDir}").mkdirs() into new File("${sourceDir}") } task copyPackages() { dependsOn unpackBallerinaDistribution def sourceDir = "${buildDir}/${distributionDir}" + "/ballerina-${version}-swan-lake/distributions/ballerina-${shortVersion}/repo" copy { from "${sourceDir}" into "${buildDir}/repo" } } task downloadBalTestProject(type: Download) { // Download nBallerina latest tag src "https://github.com/ballerina-platform/nballerina/archive/refs/heads/main.zip" onlyIfModified true dest new File("${buildDir}/nballeirna-src.zip") } task downloadBalFHIRTestProject(type: Download) { // Download nBallerina latest tag src "https://github.com/ballerina-platform/module-ballerinax-health.fhir.r4/archive/refs/tags/uscore-v1.0.5.zip" onlyIfModified true dest new File("${buildDir}/fhir-src.zip") } task unpackBalTestProject(type: Copy) { dependsOn downloadBalTestProject def sourceDir = "${buildDir}/${nbalSourceDir}" from zipTree { "${buildDir}/nballeirna-src.zip" } new File("${sourceDir}").mkdirs() into new File("${sourceDir}") } task unpackBalFHIRTestProject(type: Copy) { dependsOn downloadBalFHIRTestProject def sourceDir = "${buildDir}/${fhirSourceDir}" from zipTree { "${buildDir}/fhir-src.zip" } new File("${sourceDir}").mkdirs() into new File("${sourceDir}") } task runLSSimulatorOnnBallerina(type: JavaExec) { dependsOn copyPackages dependsOn unpackBalTestProject def extractedBalSrcDir = "${buildDir}/${nbalSourceDir}/nballerina-main/compiler" systemProperty "ls.simulation.src", "${extractedBalSrcDir}" systemProperty "ballerina.home", "$buildDir/" systemProperty "ballerina.version", "${ballerinaLangVersion}" systemProperty "ls.simulation.duration", "60" systemProperty "ls.simulation.skipGenerators", System.getProperty("ls.simulation.skipGenerators") systemProperty "LANG_REPO_BUILD", "false" jvmArgs = ['-XX:+HeapDumpOnOutOfMemoryError', "-XX:HeapDumpPath=$rootDir/dump.hprof"] maxHeapSize "1536m" group = "Execution" description = "Run the main class with JavaExecTask" classpath = sourceSets.main.runtimeClasspath main = "org.ballerinalang.langserver.simulator.EditorSimulator" } task runLSSimulatorOnFHIR(type: JavaExec) { dependsOn copyPackages dependsOn unpackBalFHIRTestProject def extractedBalSrcDir = "${buildDir}/${fhirSourceDir}/module-ballerinax-health.fhir.r4-uscore-v1.0.5/base" systemProperty "ls.simulation.src", "${extractedBalSrcDir}" systemProperty "ballerina.home", "$buildDir/" systemProperty "ballerina.version", "${ballerinaLangVersion}" systemProperty "ls.simulation.duration", "60" systemProperty "ls.simulation.skipGenerators", System.getProperty("ls.simulation.skipGenerators") systemProperty "LANG_REPO_BUILD", "false" jvmArgs = ['-XX:+HeapDumpOnOutOfMemoryError', "-XX:HeapDumpPath=$rootDir/dump.hprof"] maxHeapSize "1536m" group = "Execution" description = "Run the main class with JavaExecTask" classpath = sourceSets.main.runtimeClasspath main = "org.ballerinalang.langserver.simulator.EditorSimulator" } tasks.compileJava { doFirst { options.encoding = 'UTF-8' options.compilerArgs = [ '--module-path', classpath.asPath, ] classpath = files() } } ================================================ FILE: language-server-simulator/gradle.properties ================================================ eclipseLsp4jVersion=0.15.0 eclipseLsp4jJsonrpcVersion=0.15.0 slf4jJdk14Version=1.7.26 gsonVersion=2.9.1 slf4jApiVersion=1.7.s26 ================================================ FILE: language-server-simulator/src/main/java/module-info.java ================================================ module io.ballerina.language.server.simulator { uses org.ballerinalang.langserver.simulator.generators.CodeSnippetGenerator; requires org.eclipse.lsp4j; requires io.ballerina.language.server.commons; requires io.ballerina.language.server.core; requires org.eclipse.lsp4j.jsonrpc; requires io.ballerina.lang; requires io.ballerina.parser; requires io.ballerina.tools.api; requires com.google.gson; requires org.slf4j; } ================================================ FILE: language-server-simulator/src/main/java/org/ballerinalang/langserver/simulator/Editor.java ================================================ /* * Copyright (c) 2023, WSO2 LLC. (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 org.ballerinalang.langserver.simulator; import org.ballerinalang.langserver.BallerinaLanguageServer; import org.ballerinalang.langserver.commons.command.CommandArgument; import org.ballerinalang.langserver.util.TestUtil; import org.eclipse.lsp4j.ExecuteCommandParams; import org.eclipse.lsp4j.jsonrpc.Endpoint; import java.nio.file.Path; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * Represents the editor used by the end user, which editor consists of a set of open tabs. * * @since 2201.8.0 */ public class Editor { private final BallerinaLanguageServer languageServer; private final Endpoint endpoint; private final List tabs = new ArrayList<>(); private EditorTab activeTab; private boolean isPulled = false; private Editor(BallerinaLanguageServer languageServer, Endpoint endpoint) { this.languageServer = languageServer; this.endpoint = endpoint; } /** * Simulates opening the editor. Here we initialize the language server. * * @return Editor instance */ public static Editor open() { BallerinaLanguageServer languageServer = new BallerinaLanguageServer(); EditorOutputStream outputStream = new EditorOutputStream(); Endpoint endpoint = TestUtil.initializeLanguageSever(languageServer, outputStream); Editor editor = new Editor(languageServer, endpoint); outputStream.setEditor(editor); return editor; } public EditorTab openFile(Path filePath) { //Pull missing modules from central if (!isPulled) { CommandArgument uriArg = CommandArgument.from("doc.uri", filePath); List args = new ArrayList<>(); args.add(uriArg); ExecuteCommandParams params = new ExecuteCommandParams("PULL_MODULE", args); TestUtil.getExecuteCommandResponse(params, endpoint); isPulled = true; } EditorTab editorTab = tabs.stream() .filter(tab -> tab.filePath().equals(filePath)) .findFirst() .orElseGet(() -> { EditorTab tab = new EditorTab(filePath, endpoint, languageServer); tabs.add(tab); return tab; }); this.activeTab = editorTab; return editorTab; } public void closeFile(Path filePath) { Iterator iterator = tabs.iterator(); while (iterator.hasNext()) { EditorTab tab = iterator.next(); if (filePath.equals(tab.filePath())) { if (activeTab != null && activeTab.equals(tab)) { activeTab = null; } iterator.remove(); } } } public void closeTab(EditorTab tab) { tabs.remove(tab); if (activeTab != null && activeTab.equals(tab)) { activeTab = null; } } public void close() { this.languageServer.shutdown(); tabs.forEach(EditorTab::close); } public EditorTab activeTab() { return activeTab; } } ================================================ FILE: language-server-simulator/src/main/java/org/ballerinalang/langserver/simulator/EditorOutputStream.java ================================================ /* * Copyright (c) 2023, WSO2 LLC. (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 org.ballerinalang.langserver.simulator; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import org.eclipse.lsp4j.jsonrpc.RemoteEndpoint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.Charset; /** * A custom output stream to consume messages sent from LS to the LS client side. * * @since 2201.8.0 */ class EditorOutputStream extends ByteArrayOutputStream { private static final Logger logger = LoggerFactory.getLogger(EditorOutputStream.class); private Editor editor; /** * LSP4J invokes this method after writing a message to the stream. At that point, we should have the complete * message in the byte array. Here we consume that and reset the array. * * @throws IOException IO errors * @see RemoteEndpoint#request(String, Object) */ @Override public void flush() throws IOException { String message = this.toString(Charset.defaultCharset()); reset(); try { process(message); } catch (Throwable t) { logger.error("Error processing message", t); } } /** * Process a received message. We are interested in log message events and telemetry events to identify errors * occurred. * * @param message JSON RPC message received */ void process(String message) { String[] parts = message.replace("\r\n", "\n").split("\n"); if (parts.length > 1) { message = parts[parts.length - 1]; JsonElement jsonMsg = JsonParser.parseString(message); if (jsonMsg.isJsonObject()) { JsonObject obj = jsonMsg.getAsJsonObject(); String method = obj.get("method").getAsString(); switch (method) { case "telemetry/event": logger.info("Got telemetry event: {}", obj); if (editor != null && editor.activeTab() != null) { logger.info("Current file: {}", editor.activeTab().filePath()); logger.info("Current file content: \n{}\n========================", editor.activeTab().textDocument().toString()); } break; case "window/logMessage": logger.info("Received log message event: {}", obj); break; case "textDocument/publishDiagnostics": // pass default: // pass } } } } public void setEditor(Editor editor) { this.editor = editor; } } ================================================ FILE: language-server-simulator/src/main/java/org/ballerinalang/langserver/simulator/EditorSimulator.java ================================================ /* * Copyright (c) 2023, WSO2 LLC. (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 org.ballerinalang.langserver.simulator; import io.ballerina.compiler.syntax.tree.ModuleMemberDeclarationNode; import io.ballerina.compiler.syntax.tree.ModulePartNode; import io.ballerina.compiler.syntax.tree.NodeList; import io.ballerina.tools.text.LinePosition; import org.ballerinalang.langserver.simulator.generators.Generators; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.security.SecureRandom; import java.time.Instant; import java.util.Arrays; import java.util.List; import java.util.Random; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import java.util.stream.Stream; /** * The main class to simulate the behavior of language server. Similarly to how vscode client use LSP to send different * updates, this sends similar messages via JSON RPC to the language server. * * @since 2201.8.0 */ public class EditorSimulator { private static final Logger logger = LoggerFactory.getLogger(EditorSimulator.class); private static final String PROP_DURATION = "ls.simulation.duration"; public static final String PROP_SOURCE_DIR = "ls.simulation.src"; private static final SecureRandom random = new SecureRandom(); public static void main(String[] args) throws IOException { try { run(); } catch (Exception e) { logger.error("Error occurred while running the simulator", e); throw e; } } public static void run() throws IOException { int durationSeconds = Integer.parseInt(System.getProperty(PROP_DURATION, "60")) * 60; String projectPath = System.getProperty(PROP_SOURCE_DIR); if (projectPath == null) { throw new IllegalArgumentException("No ballerina project path provided"); } Path path = Paths.get(projectPath); logger.info("Using project: {}, path: {}", path.toString(), projectPath); List balFiles = Files.list(path) .filter(Files::isRegularFile) .filter(p -> p.getFileName() != null) .filter(p -> p.getFileName().toString().endsWith(".bal")) .collect(Collectors.toList()); if (balFiles.isEmpty()) { throw new IllegalArgumentException("No bal files found in the provided directory"); } Path modulesPath = path.resolve("modules"); if (Files.exists(modulesPath)) { Files.list(modulesPath) .filter(Files::isDirectory) .flatMap(modPath -> { try { return Files.list(modPath) .filter(Files::isRegularFile) .filter(p -> p.getFileName() != null) .filter(p -> p.getFileName().toString().endsWith(".bal")); } catch (IOException e) { logger.error("Unable to read path: {}", modPath); return Stream.empty(); } }) .forEach(balFiles::add); } logger.info("Found bal files in project: {}", balFiles.stream() .map(Path::toString).collect(Collectors.joining("\n"))); Editor editor = Editor.open(); Runtime.getRuntime().addShutdownHook(new Thread(editor::close)); long endTime = Instant.now().getEpochSecond() + durationSeconds; while (Instant.now().getEpochSecond() < endTime) { int i = random.nextInt(balFiles.size()); Path balFile = balFiles.get(i); EditorTab editorTab = editor.openFile(balFile); logger.info("Generating random code snippet"); // Get random generator type Generators.Type type = getRandomGenerator(); logger.info("Generating snippet of type: {}", type); String content = Generators.generate(type); if (type == Generators.Type.IMPORT_STATEMENT) { // Set cursor to start of the file editorTab.cursor(0, 0); } else { // Select a random place to type random code ModulePartNode modulePartNode = editorTab.syntaxTree().rootNode(); NodeList members = modulePartNode.members(); ModuleMemberDeclarationNode moduleMemberDeclarationNode = members.get(random.nextInt(members.size())); LinePosition linePosition = moduleMemberDeclarationNode.location().lineRange().startLine(); // Set cursor to start of random node editorTab.cursor(linePosition.line(), linePosition.offset()); } logger.info("Typing in editor tab: {} -> {}", editorTab, content); CompletableFuture future = CompletableFuture.runAsync(() -> { editorTab.type(content); editorTab.completions(); }); // While the snippet is being typed, check if we have reached a timeout while (!future.isDone() && Instant.now().getEpochSecond() < endTime) { logger.info("Remaining time: {}", endTime - Instant.now().getEpochSecond()); try { Thread.sleep(60 * 1000L); } catch (InterruptedException e) { Thread.currentThread().interrupt(); logger.warn("Interrupted editing", e); break; } } try { int sleepSecs = 1 + random.nextInt(5); Thread.sleep(sleepSecs * 1000L); } catch (InterruptedException e) { Thread.currentThread().interrupt(); logger.warn("Interrupted simulation", e); break; } } logger.info("Exiting..."); editor.close(); System.exit(0); } /** * Generate a random syntax tree node (top level) to be inserted to the source document. * * @return Source for a random top level node. */ public static Generators.Type getRandomGenerator() { List types = Arrays.stream(Generators.Type.values()) .filter(Generators.Type::isTopLevelNode) .collect(Collectors.toList()); // Get random generator return types.get(random.nextInt(types.size())); } } ================================================ FILE: language-server-simulator/src/main/java/org/ballerinalang/langserver/simulator/EditorTab.java ================================================ /* * Copyright (c) 2023, WSO2 LLC. (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 org.ballerinalang.langserver.simulator; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import io.ballerina.compiler.syntax.tree.SyntaxTree; import io.ballerina.projects.Document; import io.ballerina.tools.text.LinePosition; import io.ballerina.tools.text.TextDocument; import io.ballerina.tools.text.TextDocumentChange; import io.ballerina.tools.text.TextDocuments; import io.ballerina.tools.text.TextEdit; import io.ballerina.tools.text.TextRange; import org.ballerinalang.langserver.BallerinaLanguageServer; import org.ballerinalang.langserver.util.TestUtil; import org.eclipse.lsp4j.CodeActionContext; import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.jsonrpc.Endpoint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.PrintWriter; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.security.SecureRandom; import java.util.Collections; import java.util.Objects; import java.util.Optional; import java.util.concurrent.CompletableFuture; /** * Represents a tab in the {@link Editor}. Simulates the behavior of cursor and current text in the document. * * @since 2201.8.0 */ public class EditorTab { private static final Logger logger = LoggerFactory.getLogger(EditorTab.class); private final Path filePath; private final Endpoint endpoint; private final BallerinaLanguageServer languageServer; private TextDocument textDocument; private Position cursor; private final SecureRandom random = new SecureRandom(); private final PrintWriter writer = new PrintWriter(System.out, true, Charset.defaultCharset()); public EditorTab(Path filePath, Endpoint endpoint, BallerinaLanguageServer languageServer) { this.filePath = filePath; this.endpoint = endpoint; this.languageServer = languageServer; try { String content = Files.readString(filePath); this.textDocument = TextDocuments.from(content); logger.info("Opening document: {}", filePath); TestUtil.openDocument(endpoint, filePath); LinePosition linePosition = textDocument.linePositionFrom(content.length() - 1); cursor(linePosition.line(), linePosition.offset()); } catch (IOException e) { throw new IllegalArgumentException(e); } } /** * Simulates a user typing the provided content in the editor. Content is typed character by character similarly to * how a user does it. * * @param content Text content to be typed in the editor. */ public void type(String content) { int missCount = 0; for (int i = 0; i < content.length(); i++) { String typedChar = Character.toString(content.charAt(i)); int startOffset = textDocument.textPositionFrom(LinePosition.from(cursor.getLine(), cursor.getCharacter())); TextEdit edit = TextEdit.from(TextRange.from(startOffset, 0), typedChar); TextDocumentChange change = TextDocumentChange.from(new TextEdit[]{edit}); textDocument = textDocument.apply(change); LinePosition newLinePos = textDocument.linePositionFrom(startOffset + 1); try { TestUtil.didChangeDocument(this.endpoint, this.filePath, textDocument.toString()); } catch (Throwable t) { logger.error("Caught error in didChange", t); } cursor(newLinePos.line(), newLinePos.offset()); if (i % 10 == 0) { float completionPercentage = ((float) i / (float) content.length()) * 100; writer.printf("%.1f%%\r", completionPercentage); } // Get completions in the background if (i % 3 == 0) { CompletableFuture.runAsync(this::completions); CompletableFuture.runAsync(this::codeActions); } if (isDocumentNotInSync()) { missCount++; } try { Thread.sleep(100 + (long) random.nextInt(300)); } catch (InterruptedException e) { Thread.currentThread().interrupt(); logger.error("Interrupted", e); break; } } logger.info("Typed provided content in file: {} -> \n{}", filePath, content.substring(0, Math.min(20, content.length()))); logger.info("Typed {} characters with {} out of sync scenarios", content.length(), missCount); while (isDocumentNotInSync()) { logger.info("Document out of sync. Waiting 30 seconds and syncing..."); try { Thread.sleep(30 * (long) 1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } TestUtil.didChangeDocument(this.endpoint, this.filePath, textDocument.toString()); } } /** * Check if the document in this instance is similar to that is in the language server. * * @return True if the document content is not equal to that in workspace manager */ private boolean isDocumentNotInSync() { Optional document = languageServer.getWorkspaceManager().document(filePath); if (document.isPresent()) { return !document.get().textDocument().toString().equals(textDocument.toString()); } else { logger.warn("Document not found in workspace manager: {}", filePath); } return true; } /** * Get completions for the current cursor position. */ public void completions() { String completionResponse = TestUtil.getCompletionResponse(filePath.toString(), cursor, endpoint, ""); JsonObject json = JsonParser.parseString(completionResponse).getAsJsonObject(); boolean hasError = false; String resultProp = "result"; if (json.has(resultProp) && json.get(resultProp).isJsonObject()) { JsonObject result = json.getAsJsonObject(resultProp); if (!result.has("left") || !result.get("left").isJsonArray()) { hasError = true; } } else { hasError = true; } if (hasError) { logger.warn("Completion request unsuccessful! cursor: {} -> {}", filePath, cursor); } } /** * Get code actions for the current cursor position. */ public void codeActions() { CodeActionContext codeActionContext = new CodeActionContext(Collections.emptyList()); Range range = new Range(cursor, cursor); TestUtil.getCodeActionResponse(endpoint, filePath.toString(), range, codeActionContext); } public void cursor(int line, int offset) { this.cursor = new Position(line, offset); } public Position cursor() { return this.cursor; } public SyntaxTree syntaxTree() { return SyntaxTree.from(textDocument); } public TextDocument textDocument() { return textDocument; } public Path filePath() { return filePath; } public void close() { logger.info("Closing document: {}", filePath()); TestUtil.closeDocument(endpoint, filePath()); } @Override public String toString() { return "EditorTab{" + "filePath=" + filePath + ", cursor=(" + cursor.getLine() + ", " + cursor.getCharacter() + ")" + '}'; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } EditorTab editorTab = (EditorTab) o; return Objects.equals(filePath, editorTab.filePath); } @Override public int hashCode() { return Objects.hash(filePath); } } ================================================ FILE: language-server-simulator/src/main/java/org/ballerinalang/langserver/simulator/generators/ClassGenerator.java ================================================ /* * Copyright (c) 2023, WSO2 LLC. (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 org.ballerinalang.langserver.simulator.generators; import org.ballerinalang.annotation.JavaSPIService; import java.util.stream.Collectors; import java.util.stream.IntStream; /** * Class code snippet generator. * * @since 2201.8.0 */ @JavaSPIService("org.ballerinalang.langserver.org.ballerinalang.langserver.simulator.generators.CodeSnippetGenerator") public class ClassGenerator extends CodeSnippetGenerator { @Override public String generate() { return generateRandomClass(); } @Override public Generators.Type type() { return Generators.Type.CLASS; } private String generateRandomClass() { FunctionGenerator functionGenerator = Generators.getGenerator(Generators.Type.FUNCTION); int numOfFunctions = 1 + random.nextInt(100); String body = IntStream.range(0, numOfFunctions) .mapToObj(i -> functionGenerator.generateRandomFunction("fn" + i, "string")) .collect(Collectors.joining("\n")); return "class AClass {\n" + " " + body + "\n" + "}"; } } ================================================ FILE: language-server-simulator/src/main/java/org/ballerinalang/langserver/simulator/generators/CodeSnippetGenerator.java ================================================ /* * Copyright (c) 2023, WSO2 LLC. (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 org.ballerinalang.langserver.simulator.generators; import java.security.SecureRandom; import java.util.List; /** * Abstract implementation of code snippet generator for the LS simulator. * * @since 2201.8.0 */ public abstract class CodeSnippetGenerator { protected final List primitiveTypes = List.of("string", "int", "float", "decimal", "boolean"); protected SecureRandom random = new SecureRandom(); public abstract String generate(); public abstract Generators.Type type(); } ================================================ FILE: language-server-simulator/src/main/java/org/ballerinalang/langserver/simulator/generators/FunctionGenerator.java ================================================ /* * Copyright (c) 2023, WSO2 LLC. (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 org.ballerinalang.langserver.simulator.generators; import org.ballerinalang.annotation.JavaSPIService; /** * Function code snippet generator. * * @since 2201.8.0 */ @JavaSPIService("org.ballerinalang.langserver.org.ballerinalang.langserver.simulator.generators.CodeSnippetGenerator") public class FunctionGenerator extends CodeSnippetGenerator { @Override public String generate() { return generateRandomFunction(); } @Override public Generators.Type type() { return Generators.Type.FUNCTION; } public String generateRandomFunction() { String name = "fn"; String returnType = "string"; return generateRandomFunction(name, returnType); } public String generateRandomFunction(String name, String returnType) { return "\npublic function " + name + "() returns " + returnType + " {\n" + " " + getRandomFunctionBody(returnType) + "\n" + "}\n"; } public String getRandomFunctionBody(String returnType) { StatementGenerator statementGenerator = Generators.getGenerator(Generators.Type.STATEMENT); String body = ""; body += "\t" + statementGenerator.getRandomStatement(); body += "\t" + statementGenerator.getRandomStatement(); body += "\t" + statementGenerator.getRandomStatement(); body += "\treturn " + returnType + ";"; return body; } } ================================================ FILE: language-server-simulator/src/main/java/org/ballerinalang/langserver/simulator/generators/Generators.java ================================================ /* * Copyright (c) 2023, WSO2 LLC. (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 org.ballerinalang.langserver.simulator.generators; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.EnumMap; import java.util.ServiceLoader; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; /** * Factory to access {@link CodeSnippetGenerator}s. * * @since 2201.8.0 */ public class Generators { private static final Logger logger = LoggerFactory.getLogger(Generators.class); private static final String PROP_SKIPPED_GENERATORS = "ls.simulation.skipGenerators"; private static final Generators instance = new Generators(); private final EnumMap GENERATORS; private Generators() { // Get skipped generators String property = System.getProperty(PROP_SKIPPED_GENERATORS, ""); Set skippedGenerators = Stream.of(property.split(",")) .filter(type -> !type.isBlank()) .map(Type::valueOf) .collect(Collectors.toSet()); logger.info("Skipping generators of type: " + skippedGenerators); // Load generators GENERATORS = new EnumMap<>(Generators.Type.class); ServiceLoader.load(CodeSnippetGenerator.class) .forEach(generator -> { if (!skippedGenerators.contains(generator.type())) { GENERATORS.put(generator.type(), generator); } }); } /** * Generate a code snippet of provided type. * * @param type Type of the required code snippet. * @return Generated code snippet. */ public static String generate(Type type) { if (getInstance().GENERATORS.containsKey(type)) { return instance.GENERATORS.get(type).generate(); } return ""; } public static T getGenerator(Type type) { return (T) instance.GENERATORS.get(type); } public static Generators getInstance() { return instance; } /** * Different types of code snippets which can be generated. */ public enum Type { FUNCTION(true), CLASS(true), SERVICE(true), TYPE_DEFINITION(true), STATEMENT(false), MATCH_STATEMENT(false), VARIABLE_DECLARATION_STATEMENT(true), IMPORT_STATEMENT(true); private final boolean topLevelNode; Type(boolean topLevelNode) { this.topLevelNode = topLevelNode; } public boolean isTopLevelNode() { return topLevelNode; } } } ================================================ FILE: language-server-simulator/src/main/java/org/ballerinalang/langserver/simulator/generators/ImportStatementGenerator.java ================================================ /* * Copyright (c) 2023, WSO2 LLC. (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 org.ballerinalang.langserver.simulator.generators; import org.ballerinalang.annotation.JavaSPIService; import org.ballerinalang.langserver.simulator.EditorSimulator; import java.io.IOException; 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; /** * Import statement snippet generator. * * @since 2201.8.0 */ @JavaSPIService("org.ballerinalang.langserver.org.ballerinalang.langserver.simulator.generators.CodeSnippetGenerator") public class ImportStatementGenerator extends CodeSnippetGenerator { private static final String PACKAGE_NAME = "nballerina"; @Override public String generate() { //Look for modules in the source and generate import statements for them. String projectPath = System.getProperty(EditorSimulator.PROP_SOURCE_DIR); if (projectPath == null) { return ""; } Path path = Paths.get(projectPath); Path modulesPath = path.resolve("modules"); if (Files.exists(modulesPath)) { try (Stream paths = Files.list(modulesPath)) { List imports = paths.filter(Files::isDirectory) .map(p -> "import " + PACKAGE_NAME + "." + p.getFileName() + ";") .collect(Collectors.toList()); if (!imports.isEmpty()) { return imports.get(random.nextInt(imports.size())); } } catch (IOException e) { //ignore } } return ""; } @Override public Generators.Type type() { return Generators.Type.IMPORT_STATEMENT; } } ================================================ FILE: language-server-simulator/src/main/java/org/ballerinalang/langserver/simulator/generators/MatchStatementGenerator.java ================================================ /* * Copyright (c) 2023, WSO2 LLC. (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 org.ballerinalang.langserver.simulator.generators; import org.ballerinalang.annotation.JavaSPIService; /** * Match statement code snippet generator. * * @since 2201.8.0 */ @JavaSPIService("org.ballerinalang.langserver.org.ballerinalang.langserver.simulator.generators.CodeSnippetGenerator") public class MatchStatementGenerator extends CodeSnippetGenerator { /** * Generates a match statement. * * @return Match statement */ @Override public String generate() { // This statement has intentionally added syntax errors. return "\nmatch t {\n" + " () => {\n}" + " [1, 2] => {\n}" + " [1, 2, 10] => {\n}" + " [int => {\n" + " _ => {\n}" + "}\n"; } @Override public Generators.Type type() { return Generators.Type.MATCH_STATEMENT; } } ================================================ FILE: language-server-simulator/src/main/java/org/ballerinalang/langserver/simulator/generators/ServiceGenerator.java ================================================ /* * Copyright (c) 2023, WSO2 LLC. (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 org.ballerinalang.langserver.simulator.generators; import org.ballerinalang.annotation.JavaSPIService; /** * Service code snippet generator. * * @since 2201.8.0 */ @JavaSPIService("org.ballerinalang.langserver.org.ballerinalang.langserver.simulator.generators.CodeSnippetGenerator") public class ServiceGenerator extends CodeSnippetGenerator { @Override public String generate() { return generateRandomService(); } @Override public Generators.Type type() { return Generators.Type.SERVICE; } public String generateRandomService() { return "\nservice /context1 on new http:Listener(8080) {\n" + " resource function get path1(http:Caller caller, http:Request req) {\n" + " \n" + " }\n" + "}\n"; } } ================================================ FILE: language-server-simulator/src/main/java/org/ballerinalang/langserver/simulator/generators/StatementGenerator.java ================================================ /* * Copyright (c) 2023, WSO2 LLC. (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 org.ballerinalang.langserver.simulator.generators; import org.ballerinalang.annotation.JavaSPIService; /** * Statement code snippet generator. * * @since 2201.8.0 */ @JavaSPIService("org.ballerinalang.langserver.org.ballerinalang.langserver.simulator.generators.CodeSnippetGenerator") public class StatementGenerator extends CodeSnippetGenerator { @Override public String generate() { return getRandomStatement(); } @Override public Generators.Type type() { return Generators.Type.STATEMENT; } public String getRandomStatement() { switch (random.nextInt(2)) { case 0: return Generators.generate(Generators.Type.MATCH_STATEMENT); case 1: default: return Generators.generate(Generators.Type.VARIABLE_DECLARATION_STATEMENT); } } } ================================================ FILE: language-server-simulator/src/main/java/org/ballerinalang/langserver/simulator/generators/TypeDefinitionGenerator.java ================================================ /* * Copyright (c) 2023, WSO2 LLC. (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 org.ballerinalang.langserver.simulator.generators; import org.ballerinalang.annotation.JavaSPIService; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * Type definition code snippet generator. * * @since 2201.8.0 */ @JavaSPIService("org.ballerinalang.langserver.org.ballerinalang.langserver.simulator.generators.CodeSnippetGenerator") public class TypeDefinitionGenerator extends CodeSnippetGenerator { private final List generatedTypeDefNames = new ArrayList<>(); private int typeCount = 0; @Override public String generate() { switch (random.nextInt(3)) { case 1: return generateUnionType(); case 2: return generateRecordType(); case 3: default: return generateObjectTypeDef(); } } public String generateRecordType() { String typeName = "Rec" + typeCount; List fields = new ArrayList<>(); for (int i = 0; i < 2 + random.nextInt(100); i++) { String field; if (random.nextBoolean() || generatedTypeDefNames.isEmpty()) { field = String.format("\t%s field%d;", primitiveTypes.get(random.nextInt(primitiveTypes.size())), i); } else { field = String.format("\t%s field%d;", generatedTypeDefNames.get(random.nextInt(generatedTypeDefNames.size())), i); } fields.add(field); } typeCount++; generatedTypeDefNames.add(typeName); return String.format("%ntype %s {|%n%s%n|};%n", typeName, String.join("\n", fields)); } public String generateUnionType() { // Member types Set memberTypes = new HashSet<>(); for (int i = 0; i < 2 + random.nextInt(3); i++) { String memberType; do { if (random.nextBoolean() || generatedTypeDefNames.isEmpty()) { memberType = primitiveTypes.get(random.nextInt(primitiveTypes.size())); } else { memberType = generatedTypeDefNames.get(random.nextInt(generatedTypeDefNames.size())); } } while (memberTypes.contains(memberType)); memberTypes.add(memberType); } String typeName = "Type" + typeCount; typeCount++; generatedTypeDefNames.add(typeName); return String.format("%ntype %s %s;%n", typeName, String.join(" | ", memberTypes)); } public String generateObjectTypeDef() { String typeName = "ObjectDef" + typeCount; typeCount++; generatedTypeDefNames.add(typeName); return "\ntype " + typeName + " object {\n" + "\n\tpublic function doSomething() returns UnkType;\n" + "};\n"; } @Override public Generators.Type type() { return Generators.Type.TYPE_DEFINITION; } } ================================================ FILE: language-server-simulator/src/main/java/org/ballerinalang/langserver/simulator/generators/VarDeclarationStatementGenerator.java ================================================ /* * Copyright (c) 2023, WSO2 LLC. (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 org.ballerinalang.langserver.simulator.generators; import org.ballerinalang.annotation.JavaSPIService; /** * Variable declaration code snippet generator. * * @since 2.0.0 */ @JavaSPIService("org.ballerinalang.langserver.org.ballerinalang.langserver.simulator.generators.CodeSnippetGenerator") public class VarDeclarationStatementGenerator extends CodeSnippetGenerator { private int varCount = 0; public String generate() { varCount++; return String.format("%n%s %s = createVar();%n", primitiveTypes.get(random.nextInt(primitiveTypes.size())), "myVar" + varCount); } @Override public Generators.Type type() { return Generators.Type.VARIABLE_DECLARATION_STATEMENT; } } ================================================ FILE: language-server-simulator/src/main/resources/META-INF/services/org.ballerinalang.langserver.simulator.generators.CodeSnippetGenerator ================================================ org.ballerinalang.langserver.simulator.generators.VarDeclarationStatementGenerator org.ballerinalang.langserver.simulator.generators.TypeDefinitionGenerator org.ballerinalang.langserver.simulator.generators.StatementGenerator org.ballerinalang.langserver.simulator.generators.ImportStatementGenerator org.ballerinalang.langserver.simulator.generators.ServiceGenerator org.ballerinalang.langserver.simulator.generators.ClassGenerator org.ballerinalang.langserver.simulator.generators.MatchStatementGenerator org.ballerinalang.langserver.simulator.generators.FunctionGenerator ================================================ FILE: project-api-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. ~ */ apply from: "$rootDir/gradle/javaProject.gradle" description = 'Ballerina - Central Tests' 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:ballerina-lang:${ballerinaLangVersion}" testImplementation "org.ballerinalang:jballerina-debugger-integration-test:${ballerinaLangVersion}" testImplementation "org.ballerinalang:ballerina-test-utils:${ballerinaLangVersion}" testImplementation "org.eclipse.lsp4j:org.eclipse.lsp4j.debug:${lsp4jDebugVersion}" testImplementation "com.fasterxml.jackson.core:jackson-databind:2.12.3" testImplementation "com.squareup.okhttp3:okhttp:3.14.0" 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 centralTests { dependsOn configurations.jBallerinaDistribution dependsOn configurations.ballerinaDistribution 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-${System.getProperty("short.version")}.zip" systemProperty "code.name", "${codeName}" useTestNG() { suites 'src/test/resources/testng.xml' } testLogging { showStandardStreams = true } } } task checkURL { doLast { try { URL url = new URL("${devIdpUrl}") HttpURLConnection connection = (HttpURLConnection) url.openConnection() connection.setRequestMethod("GET") connection.connect() int code = connection.getResponseCode() if (code == 200) { logger.lifecycle("Connection to ${devIdpUrl} is successful. Running tests...") ext.isURLAvailable = true } else { logger.lifecycle("Connection to ${devIdpUrl} is not successful. Skipping tests...") logger.lifecycle("-----------------------------------------") logger.lifecycle(" WARNING: Skipping the project api tests") logger.lifecycle("-----------------------------------------") ext.isURLAvailable = false } } catch(Exception ex) { logger.lifecycle("Connection to ${devIdpUrl} is not successful. Exception while connecting to " + "${devIdpUrl}. Skipping tests...") logger.lifecycle("-----------------------------------------") logger.lifecycle(" WARNING: Skipping the project api tests") logger.lifecycle("-----------------------------------------") ext.isURLAvailable = false } } } test { dependsOn checkURL onlyIf { checkURL.isURLAvailable } dependsOn centralTests } ================================================ FILE: project-api-tests/src/test/java/org/ballerina/projectapi/BalToolTest.java ================================================ /* * Copyright (c) 2024, 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.projectapi; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import io.ballerina.projects.BalToolsManifest; import io.ballerina.projects.BalToolsToml; import io.ballerina.projects.JvmTarget; import io.ballerina.projects.internal.BalToolsManifestBuilder; import io.ballerina.projects.util.ProjectUtils; import org.apache.commons.lang3.tuple.Pair; import org.testng.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeGroups; import org.testng.annotations.Test; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import static io.ballerina.projects.util.ProjectConstants.BALA_DIR_NAME; import static io.ballerina.projects.util.ProjectConstants.CENTRAL_REPOSITORY_CACHE_NAME; import static io.ballerina.projects.util.ProjectConstants.REPOSITORIES_DIR; import static org.ballerina.projectapi.CentralTestUtils.OUTPUT_NOT_CONTAINS_EXP_MSG; import static org.ballerina.projectapi.CentralTestUtils.createSettingToml; import static org.ballerina.projectapi.CentralTestUtils.getEnvVariables; import static org.ballerina.projectapi.CentralTestUtils.getString; import static org.ballerina.projectapi.TestUtils.DISTRIBUTION_FILE_NAME; import static org.ballerina.projectapi.TestUtils.OUTPUT_CONTAIN_ERRORS; import static org.ballerina.projectapi.TestUtils.executeCommand; import static org.ballerina.projectapi.TestUtils.executeHelpCommand; import static org.ballerina.projectapi.TestUtils.executePackCommand; import static org.ballerina.projectapi.TestUtils.executePushCommand; import static org.ballerina.projectapi.TestUtils.executeToolCommand; public class BalToolTest { private Path tempWorkspaceDirectory; private Map toolEnvironments; static final String ORG_NAME = "bctestorg"; static final String TOOL_ID = "disttest"; static final String PACKAGE_NAME = "disttestpackage"; static final String NON_EXISTING_TOOL = "disttest2"; static final String LATEST_VERSION = "1.1.0"; static final String SPECIFIC_VERSION = "1.0.0"; static final String PATCH_VERSION = "1.0.1"; static final String NON_EXISTING_VERSION = "1.0.3"; static final String INCOMPATIBLE_DIST_VERSION = "1.0.4"; static final String PULL = "pull"; static final String REMOVE = "remove"; static final String UPDATE = "update"; static final String USE = "use"; static final String LIST = "list"; static final String SEARCH = "search"; @BeforeClass() public void setUp() throws IOException, InterruptedException { TestUtils.setupDistributions(); Path tempHomeDirectory = Files.createTempDirectory("bal-test-integration-packaging-home-"); this.tempWorkspaceDirectory = Files.createTempDirectory("bal-test-integration-packaging-workspace-"); createSettingToml(tempHomeDirectory); this.toolEnvironments = setToolEnvironmentsForSubCommands(); Map envVariables = TestUtils.addEnvVariables(getEnvVariables(), tempHomeDirectory); copyTestResourcesToTempWorkspace(); pushToolPackages(envVariables); } @BeforeGroups(value = "pull") public void setupPullTests() { // Remove the tool from bal-tools.toml if exists BalToolsToml balToolsToml = BalToolsToml.from(toolEnvironments.get(ToolSubCommand.PULL).balToolsTomlPath); BalToolsManifest balToolsManifest = BalToolsManifestBuilder.from(balToolsToml).build(); balToolsManifest.removeTool(TOOL_ID); balToolsToml.modify(balToolsManifest); // Delete the tool from local cache if exists Path toolPath = toolEnvironments.get(ToolSubCommand.PULL).centralCachePath .resolve(Path.of(ORG_NAME, PACKAGE_NAME)); if (Files.exists(toolPath)) { ProjectUtils.deleteDirectory(toolPath); } } @Test(description = "Pull a tool without specifying a version", groups = {"pull"}) public void testPullToolWithoutVersion() throws IOException, InterruptedException { Process cmdExec = executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(PULL, TOOL_ID)), toolEnvironments.get(ToolSubCommand.PULL).envVariables); String cmdErrors = getString(cmdExec.getErrorStream()); if (!cmdErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + cmdErrors); } // Validate the command output String cmdOutput = getString(cmdExec.getInputStream()); String expectedOutput = readExpectedCmdOutsAsString("tool-pull-without-version.txt"); if (!cmdOutput.contains(expectedOutput)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedOutput + "\nactual output:" + cmdOutput); } // Check for tool availability in bal-tools.toml BalToolsToml balToolsToml = BalToolsToml.from(toolEnvironments.get(ToolSubCommand.PULL).balToolsTomlPath); Optional toolOpt = BalToolsManifestBuilder.from(balToolsToml).build() .getActiveTool(TOOL_ID); if (toolOpt.isEmpty()) { Assert.fail("Tool " + TOOL_ID + " is not available in bal-tools.toml"); } BalToolsManifest.Tool tool = toolOpt.get(); Assert.assertEquals(tool.id(), TOOL_ID); Assert.assertEquals(tool.version(), LATEST_VERSION); Assert.assertEquals(tool.org(), ORG_NAME); Assert.assertEquals(tool.name(), PACKAGE_NAME); // Check for tool availability in local cache Path toolVersionCachePath = toolEnvironments.get(ToolSubCommand.PULL).centralCachePath .resolve(Path.of(tool.org(), tool.name(), tool.version())); if (!Files.exists(toolVersionCachePath)) { Assert.fail("Tool " + TOOL_ID + ":" + LATEST_VERSION + " is not available in local cache"); } // Execute the tool Pair outputs = executeHelpFlagOfTool( TOOL_ID, toolEnvironments.get(ToolSubCommand.PULL).envVariables); Assert.assertEquals(outputs.getLeft(), ""); Assert.assertEquals(outputs.getRight(), readExpectedCmdOutsAsString("tool-execute-specific-help-1.1.0.txt")); } @Test(description = "Pull a tool again without specifying a version", dependsOnMethods = "testPullToolWithoutVersion", groups = {"pull"}) public void testPullToolAgainWithoutVersion() throws IOException, InterruptedException { Process cmdExec = executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(PULL, TOOL_ID)), toolEnvironments.get(ToolSubCommand.PULL).envVariables); String cmdErrors = getString(cmdExec.getErrorStream()); if (!cmdErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + cmdErrors); } // Validate the command output String cmdOutput = getString(cmdExec.getInputStream()); String expectedOutput = readExpectedCmdOutsAsString( "tool-pull-again-without-version.txt"); if (!cmdOutput.contains(expectedOutput)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedOutput + "\nactual output:" + cmdOutput); } // Check for tool availability in bal-tools.toml BalToolsToml balToolsToml = BalToolsToml.from(toolEnvironments.get(ToolSubCommand.PULL).balToolsTomlPath); Optional toolOpt = BalToolsManifestBuilder.from(balToolsToml).build() .getActiveTool(TOOL_ID); if (toolOpt.isEmpty()) { Assert.fail("Tool " + TOOL_ID + " is not available in bal-tools.toml"); } BalToolsManifest.Tool tool = toolOpt.get(); Assert.assertEquals(tool.id(), TOOL_ID); Assert.assertEquals(tool.version(), LATEST_VERSION); Assert.assertEquals(tool.org(), ORG_NAME); Assert.assertEquals(tool.name(), PACKAGE_NAME); // Check for tool availability in local cache Path toolVersionCachePath = toolEnvironments.get(ToolSubCommand.PULL).centralCachePath .resolve(Path.of(tool.org(), tool.name(), tool.version())); if (!Files.exists(toolVersionCachePath)) { Assert.fail("Tool " + TOOL_ID + ":" + LATEST_VERSION + " is not available in local cache"); } // Execute the tool Pair outputs = executeHelpFlagOfTool( TOOL_ID, toolEnvironments.get(ToolSubCommand.PULL).envVariables); Assert.assertEquals(outputs.getLeft(), ""); Assert.assertEquals(outputs.getRight(), readExpectedCmdOutsAsString("tool-execute-specific-help-1.1.0.txt")); } @Test(description = "Pull a tool with a specific version", dependsOnMethods = {"testPullToolAgainWithoutVersion"}, groups = {"pull"}) public void testPullToolWithASpecificVersion() throws IOException, InterruptedException { String toolIdAndVersion = TOOL_ID + ":" + SPECIFIC_VERSION; Process cmdExec = executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(PULL, toolIdAndVersion)), toolEnvironments.get(ToolSubCommand.PULL).envVariables); String cmdErrors = getString(cmdExec.getErrorStream()); if (!cmdErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + cmdErrors); } // Validate the command output String cmdOutput = getString(cmdExec.getInputStream()); String expectedOutput = readExpectedCmdOutsAsString( "tool-pull-with-specific-version.txt"); if (!cmdOutput.contains(expectedOutput)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedOutput + "\nactual output:" + cmdOutput); } // Check for tool availability in bal-tools.toml BalToolsToml balToolsToml = BalToolsToml.from(toolEnvironments.get(ToolSubCommand.PULL).balToolsTomlPath); Optional toolOpt = BalToolsManifestBuilder.from(balToolsToml).build() .getActiveTool(TOOL_ID); if (toolOpt.isEmpty()) { Assert.fail("Tool " + TOOL_ID + " is not available in bal-tools.toml"); } BalToolsManifest.Tool tool = toolOpt.get(); Assert.assertEquals(tool.id(), TOOL_ID); Assert.assertEquals(tool.version(), SPECIFIC_VERSION); Assert.assertEquals(tool.org(), ORG_NAME); Assert.assertEquals(tool.name(), PACKAGE_NAME); // Check for tool availability in local cache Path toolVersionCachePath = toolEnvironments.get(ToolSubCommand.PULL).centralCachePath .resolve(Path.of(tool.org(), tool.name(), tool.version())); if (!Files.exists(toolVersionCachePath)) { Assert.fail("Tool " + TOOL_ID + ":" + SPECIFIC_VERSION + " is not available in local cache"); } // Execute the tool Pair outputs = executeHelpFlagOfTool( TOOL_ID, toolEnvironments.get(ToolSubCommand.PULL).envVariables); Assert.assertEquals(outputs.getLeft(), ""); Assert.assertEquals(outputs.getRight(), readExpectedCmdOutsAsString( "tool-execute-specific-help-1.0.0.txt")); } @Test(description = "Pull a tool again with a specific version", dependsOnMethods = "testPullToolWithASpecificVersion", groups = {"pull"}) public void testPullToolAgainWithASpecificVersion() throws IOException, InterruptedException { String toolIdAndVersion = TOOL_ID + ":" + SPECIFIC_VERSION; Process cmdExec = executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(PULL, toolIdAndVersion)), toolEnvironments.get(ToolSubCommand.PULL).envVariables); String cmdErrors = getString(cmdExec.getErrorStream()); if (!cmdErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + cmdErrors); } // Validate the command output String cmdOutput = getString(cmdExec.getInputStream()); String expectedOutput = readExpectedCmdOutsAsString( "tool-pull-again-with-specific-version.txt"); if (!cmdOutput.contains(expectedOutput)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedOutput + "\nactual output:" + cmdOutput); } // Check for tool availability in bal-tools.toml BalToolsToml balToolsToml = BalToolsToml.from(toolEnvironments.get(ToolSubCommand.PULL).balToolsTomlPath); Optional toolOpt = BalToolsManifestBuilder.from(balToolsToml).build() .getActiveTool(TOOL_ID); if (toolOpt.isEmpty()) { Assert.fail("Tool " + TOOL_ID + " is not available in bal-tools.toml"); } BalToolsManifest.Tool tool = toolOpt.get(); Assert.assertEquals(tool.id(), TOOL_ID); Assert.assertEquals(tool.version(), SPECIFIC_VERSION); Assert.assertEquals(tool.org(), ORG_NAME); Assert.assertEquals(tool.name(), PACKAGE_NAME); // Check for tool availability in local cache Path toolVersionCachePath = toolEnvironments.get(ToolSubCommand.PULL).centralCachePath .resolve(Path.of(tool.org(), tool.name(), tool.version())); if (!Files.exists(toolVersionCachePath)) { Assert.fail("Tool " + TOOL_ID + ":" + SPECIFIC_VERSION + " is not available in local cache"); } // Execute the tool Pair outputs = executeHelpFlagOfTool( TOOL_ID, toolEnvironments.get(ToolSubCommand.PULL).envVariables); Assert.assertEquals(outputs.getLeft(), ""); Assert.assertEquals(outputs.getRight(), readExpectedCmdOutsAsString( "tool-execute-specific-help-1.0.0.txt")); } @Test(description = "Pull a tool with a non existing version", dependsOnMethods = "testPullToolWithASpecificVersion", groups = {"pull"}) public void testPullToolWithANonExistingVersion() throws IOException, InterruptedException { String toolIdAndVersion = TOOL_ID + ":" + NON_EXISTING_VERSION; Process cmdExec = executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(PULL, toolIdAndVersion)), toolEnvironments.get(ToolSubCommand.PULL).envVariables); String cmdErrors = getString(cmdExec.getErrorStream()); String expectedOutput = readExpectedCmdOutsAsString( "tool-pull-with-non-existing-version.txt"); if (!cmdErrors.contains(expectedOutput)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedOutput + "\nactual output:" + cmdErrors); } // Check for tool availability in bal-tools.toml BalToolsToml balToolsToml = BalToolsToml.from(toolEnvironments.get(ToolSubCommand.PULL).balToolsTomlPath); Optional toolOpt = BalToolsManifestBuilder.from(balToolsToml).build() .getTool(TOOL_ID, NON_EXISTING_VERSION, null); if (toolOpt.isPresent()) { Assert.fail("Tool " + toolIdAndVersion + " should not be in bal-tools.toml"); } // Check for tool availability in local cache Path toolVersionCachePath = toolEnvironments.get(ToolSubCommand.PULL).centralCachePath .resolve(Path.of(ORG_NAME, TOOL_ID, NON_EXISTING_VERSION)); if (Files.exists(toolVersionCachePath)) { Assert.fail("Tool " + TOOL_ID + ":" + NON_EXISTING_VERSION + " should not be available in local cache"); } // Execute the tool Pair outputs = executeHelpFlagOfTool( TOOL_ID, toolEnvironments.get(ToolSubCommand.PULL).envVariables); Assert.assertEquals(outputs.getLeft(), ""); Assert.assertEquals(outputs.getRight(), readExpectedCmdOutsAsString( "tool-execute-specific-help-1.0.0.txt")); } @Test(description = "Pull a tool version with incompatible distribution", dependsOnMethods = "testPullToolWithASpecificVersion", groups = {"pull"}) public void testPullAToolVersionWithIncompatibleDistribution() throws IOException, InterruptedException { String toolIdAndVersion = TOOL_ID + ":" + INCOMPATIBLE_DIST_VERSION; Process cmdExec = executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(PULL, toolIdAndVersion)), toolEnvironments.get(ToolSubCommand.PULL).envVariables); String cmdErrors = getString(cmdExec.getErrorStream()); String expectedOutput = readExpectedCmdOutsAsString( "tool-pull-with-incompatible-dist.txt"); if (!cmdErrors.contains(expectedOutput)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedOutput + "\nactual output:" + cmdErrors); } // Execute the tool Pair outputs = executeHelpFlagOfTool( TOOL_ID, toolEnvironments.get(ToolSubCommand.PULL).envVariables); Assert.assertEquals(outputs.getLeft(), ""); Assert.assertEquals(outputs.getRight(), readExpectedCmdOutsAsString( "tool-execute-specific-help-1.0.0.txt")); } @BeforeGroups(value = "remove") public void setupRemoveTests() throws IOException, InterruptedException { // Pull a specific version of a tool String specificToolVersion = TOOL_ID + ":" + SPECIFIC_VERSION; executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(PULL, specificToolVersion)), toolEnvironments.get(ToolSubCommand.REMOVE).envVariables); // Pull the latest tool version executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(PULL, TOOL_ID)), toolEnvironments.get(ToolSubCommand.REMOVE).envVariables); // Pull tool version with incompatible distribution String incompToolVersion = TOOL_ID + ":" + INCOMPATIBLE_DIST_VERSION; executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(PULL, incompToolVersion)), toolEnvironments.get(ToolSubCommand.REMOVE).envVariables); // Add the dist incompatible version to the toml file BalToolsToml balToolsToml = BalToolsToml.from(toolEnvironments.get(ToolSubCommand.REMOVE).balToolsTomlPath); BalToolsManifest balToolsManifest = BalToolsManifestBuilder.from(balToolsToml).build(); balToolsManifest.addTool(TOOL_ID, ORG_NAME, PACKAGE_NAME, INCOMPATIBLE_DIST_VERSION, false, null); balToolsToml.modify(balToolsManifest); } @Test(description = "Remove a non existing tool", groups = {"remove"}) public void testRemoveANonExistingTool() throws IOException, InterruptedException { Process cmdExec = executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(REMOVE, NON_EXISTING_TOOL)), toolEnvironments.get(ToolSubCommand.REMOVE).envVariables); // Validate the command output String cmdErrors = getString(cmdExec.getErrorStream()); String expectedOutput = readExpectedCmdOutsAsString( "tool-remove-non-existing-tool.txt"); if (!cmdErrors.contains(expectedOutput)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedOutput + "\nactual output:" + cmdErrors); } // Execute the tool Pair outputs = executeHelpFlagOfTool( NON_EXISTING_TOOL, toolEnvironments.get(ToolSubCommand.REMOVE).envVariables); Assert.assertEquals( outputs.getLeft(), readExpectedCmdOutsAsString("tool-execute-unknown-cmd-non-existing.txt")); Assert.assertEquals(outputs.getRight(), ""); } @Test(description = "Remove a non existing version of a tool", groups = {"remove"}) public void testRemoveANonExistingVersionOfATool() throws IOException, InterruptedException { String toolIdAndVersion = TOOL_ID + ":" + NON_EXISTING_VERSION; Process cmdExec = executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(REMOVE, toolIdAndVersion)), toolEnvironments.get(ToolSubCommand.REMOVE).envVariables); // Validate the command output String cmdErrors = getString(cmdExec.getErrorStream()); String expectedOutput = readExpectedCmdOutsAsString( "tool-remove-non-existing-version.txt"); if (!cmdErrors.contains(expectedOutput)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedOutput + "\nactual output:" + cmdErrors); } // Execute the tool Pair outputs = executeHelpFlagOfTool( TOOL_ID, toolEnvironments.get(ToolSubCommand.REMOVE).envVariables); Assert.assertEquals(outputs.getLeft(), ""); Assert.assertEquals(outputs.getRight(), readExpectedCmdOutsAsString("tool-execute-specific-help-1.1.0.txt")); } @Test(description = "Remove a tool version with incompatible distribution", groups = {"remove"}) public void testRemoveToolVersionWithIncompatibleDistribution() throws IOException, InterruptedException { String toolIdAndVersion = TOOL_ID + ":" + INCOMPATIBLE_DIST_VERSION; Process cmdExec = executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(REMOVE, toolIdAndVersion)), toolEnvironments.get(ToolSubCommand.REMOVE).envVariables); String cmdErrors = getString(cmdExec.getErrorStream()); String expectedOutput = readExpectedCmdOutsAsString( "tool-remove-with-incompatible-dist.txt"); if (!cmdErrors.contains(expectedOutput)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedOutput + "\nactual output:" + cmdErrors); } // Execute the tool Pair outputs = executeHelpFlagOfTool( TOOL_ID, toolEnvironments.get(ToolSubCommand.REMOVE).envVariables); Assert.assertEquals(outputs.getLeft(), ""); Assert.assertEquals(outputs.getRight(), readExpectedCmdOutsAsString("tool-execute-specific-help-1.1.0.txt")); } @Test(description = "Remove a tool version with incompatible distribution", groups = {"remove"}) public void testRemoveToolActiveVersion() throws IOException, InterruptedException { // Make the latest version active BalToolsToml balToolsToml = BalToolsToml.from(toolEnvironments.get(ToolSubCommand.REMOVE).balToolsTomlPath); BalToolsManifest balToolsManifest = BalToolsManifestBuilder.from(balToolsToml).build(); balToolsManifest.setActiveToolVersion(TOOL_ID, LATEST_VERSION, null); balToolsToml.modify(balToolsManifest); // Remove the active version String toolIdAndVersion = TOOL_ID + ":" + LATEST_VERSION; Process cmdExec = executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(REMOVE, toolIdAndVersion)), toolEnvironments.get(ToolSubCommand.REMOVE).envVariables); String cmdErrors = getString(cmdExec.getErrorStream()); String expectedOutput = readExpectedCmdOutsAsString("tool-remove-active-version.txt"); if (!cmdErrors.contains(expectedOutput)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedOutput + "\nactual output:" + cmdErrors); } // Execute the tool Pair outputs = executeHelpFlagOfTool( TOOL_ID, toolEnvironments.get(ToolSubCommand.REMOVE).envVariables); Assert.assertEquals(outputs.getLeft(), ""); Assert.assertEquals(outputs.getRight(), readExpectedCmdOutsAsString("tool-execute-specific-help-1.1.0.txt")); } @Test(description = "Remove a specific tool version", dependsOnMethods = {"testRemoveANonExistingVersionOfATool", "testRemoveToolVersionWithIncompatibleDistribution", "testRemoveToolActiveVersion"}, groups = {"remove"}) public void testRemoveASpecificToolVersion() throws IOException, InterruptedException { // Make the latest version active BalToolsToml balToolsToml = BalToolsToml.from(toolEnvironments.get(ToolSubCommand.REMOVE).balToolsTomlPath); BalToolsManifest balToolsManifest = BalToolsManifestBuilder.from(balToolsToml).build(); balToolsManifest.setActiveToolVersion(TOOL_ID, LATEST_VERSION, null); balToolsToml.modify(balToolsManifest); String toolIdAndVersion = TOOL_ID + ":" + SPECIFIC_VERSION; // Check for tool availability in bal-tools.toml Optional toolOpt = balToolsManifest.getTool(TOOL_ID, SPECIFIC_VERSION, null); if (toolOpt.isEmpty()) { Assert.fail("Tool '" + toolIdAndVersion + "' is not available to be removed"); } Process cmdExec = executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(REMOVE, toolIdAndVersion)), toolEnvironments.get(ToolSubCommand.REMOVE).envVariables); String cmdErrors = getString(cmdExec.getErrorStream()); if (!cmdErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + cmdErrors); } // Validate the command output String cmdOutput = getString(cmdExec.getInputStream()); String expectedOutput = readExpectedCmdOutsAsString( "tool-remove-specific-version.txt"); if (!cmdOutput.contains(expectedOutput)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedOutput + "\nactual output:" + cmdOutput); } // Check for tool availability in bal-tools.toml balToolsToml = BalToolsToml.from(toolEnvironments.get(ToolSubCommand.REMOVE).balToolsTomlPath); toolOpt = BalToolsManifestBuilder.from(balToolsToml).build().getTool(TOOL_ID, SPECIFIC_VERSION, null); if (toolOpt.isPresent()) { Assert.fail("Tool '" + toolIdAndVersion + "' is available in bal-tools.toml"); } // Check for tool availability in local cache Path toolVersionCachePath = toolEnvironments.get(ToolSubCommand.REMOVE).centralCachePath .resolve(Path.of(ORG_NAME, PACKAGE_NAME, SPECIFIC_VERSION)); if (Files.exists(toolVersionCachePath)) { Assert.fail("Tool '" + toolIdAndVersion + "' is available in local cache"); } // Execute the tool Pair outputs = executeHelpFlagOfTool( TOOL_ID, toolEnvironments.get(ToolSubCommand.REMOVE).envVariables); Assert.assertEquals(outputs.getLeft(), ""); Assert.assertEquals(outputs.getRight(), readExpectedCmdOutsAsString("tool-execute-specific-help-1.1.0.txt")); } @Test(description = "Remove all tool versions", dependsOnMethods = {"testRemoveASpecificToolVersion"}, groups = {"remove"}) public void testRemoveAllToolVersions() throws IOException, InterruptedException { // Check for tool availability in bal-tools.toml BalToolsToml balToolsToml = BalToolsToml.from(toolEnvironments.get(ToolSubCommand.REMOVE).balToolsTomlPath); Optional toolOpt = BalToolsManifestBuilder.from(balToolsToml).build() .getActiveTool(TOOL_ID); if (toolOpt.isEmpty()) { Assert.fail("Tool '" + TOOL_ID + "' is not available to be removed"); } // Remove all versions of the tool Process cmdExec = executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(REMOVE, TOOL_ID)), toolEnvironments.get(ToolSubCommand.REMOVE).envVariables); String cmdErrors = getString(cmdExec.getErrorStream()); if (!cmdErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + cmdErrors); } // Validate the command output String cmdOutput = getString(cmdExec.getInputStream()); String expectedOutput = readExpectedCmdOutsAsString("tool-remove-all.txt"); if (!cmdOutput.contains(expectedOutput)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedOutput + "\nactual output:" + cmdOutput); } // Check for tool availability in bal-tools.toml balToolsToml = BalToolsToml.from(toolEnvironments.get(ToolSubCommand.REMOVE).balToolsTomlPath); toolOpt = BalToolsManifestBuilder.from(balToolsToml).build().getActiveTool(TOOL_ID); if (toolOpt.isPresent()) { Assert.fail("Tool " + TOOL_ID + " is available in bal-tools.toml"); } // Check for tool availability in local cache Path toolVersionCachePath = toolEnvironments.get(ToolSubCommand.REMOVE).centralCachePath .resolve(Path.of(ORG_NAME, PACKAGE_NAME)); if (Files.exists(toolVersionCachePath)) { Assert.fail("Tool '" + TOOL_ID + "' is available in local cache"); } // Execute the tool Pair outputs = executeHelpFlagOfTool( TOOL_ID, toolEnvironments.get(ToolSubCommand.REMOVE).envVariables); Assert.assertEquals(outputs.getLeft(), readExpectedCmdOutsAsString("tool-execute-unknown-cmd.txt")); Assert.assertEquals(outputs.getRight(), ""); } @BeforeGroups(value = "update") public void setupUpdateTests() throws IOException, InterruptedException { // Remove all versions of the tool Process cmdExec = executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(REMOVE, TOOL_ID)), toolEnvironments.get(ToolSubCommand.UPDATE).envVariables); getString(cmdExec.getErrorStream()); // Pull an older version of the tool String toolIdAndVersion = TOOL_ID + ":" + SPECIFIC_VERSION; executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(PULL, toolIdAndVersion)), toolEnvironments.get(ToolSubCommand.UPDATE).envVariables); } @Test(description = "Update a non-existing tool", groups = {"update"}) public void testUpdateNonExistingTool() throws IOException, InterruptedException { Process cmdExec = executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(UPDATE, NON_EXISTING_TOOL)), toolEnvironments.get(ToolSubCommand.UPDATE).envVariables); String cmdErrors = getString(cmdExec.getErrorStream()); String expectedOutput = readExpectedCmdOutsAsString("tool-update-non-existing.txt"); if (!cmdErrors.contains(expectedOutput)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedOutput + "\nactual output:" + cmdErrors); } // Check for tool availability in bal-tools.toml BalToolsToml balToolsToml = BalToolsToml.from(toolEnvironments.get(ToolSubCommand.UPDATE).balToolsTomlPath); Optional toolOpt = BalToolsManifestBuilder.from(balToolsToml).build() .getActiveTool(NON_EXISTING_TOOL); if (toolOpt.isPresent()) { Assert.fail("Tool " + NON_EXISTING_TOOL + " should not be available in bal-tools.toml"); } // Execute the tool Pair outputs = executeHelpFlagOfTool( NON_EXISTING_TOOL, toolEnvironments.get(ToolSubCommand.UPDATE).envVariables); Assert.assertEquals( outputs.getLeft(), readExpectedCmdOutsAsString("tool-execute-unknown-cmd-non-existing.txt")); Assert.assertEquals(outputs.getRight(), ""); } @Test(description = "Update a tool with new patch and minor versions", groups = {"update"}) public void testUpdateToolWithNewPatchAndMinor() throws IOException, InterruptedException { // Check for tool availability in bal-tools.toml BalToolsToml balToolsToml = BalToolsToml.from(toolEnvironments.get(ToolSubCommand.UPDATE).balToolsTomlPath); Optional toolOpt = BalToolsManifestBuilder.from(balToolsToml).build() .getActiveTool(TOOL_ID); String toolIdAndVersion = TOOL_ID + ":" + SPECIFIC_VERSION; if (toolOpt.isEmpty()) { Assert.fail("Tool '" + toolIdAndVersion + "' should be the current version in bal-tools.toml before update command"); } // Update the tool Process cmdExec = executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(UPDATE, TOOL_ID)), toolEnvironments.get(ToolSubCommand.UPDATE).envVariables); String cmdErrors = getString(cmdExec.getErrorStream()); if (!cmdErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + cmdErrors); } // Validate the command output String cmdOutput = getString(cmdExec.getInputStream()); String expectedOutput = readExpectedCmdOutsAsString( "tool-update-with-new-patch-and-minor.txt"); if (!cmdOutput.contains(expectedOutput)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedOutput + "\nactual output:" + cmdOutput); } // Check for tool availability in bal-tools.toml balToolsToml = BalToolsToml.from(toolEnvironments.get(ToolSubCommand.UPDATE).balToolsTomlPath); toolOpt = BalToolsManifestBuilder.from(balToolsToml).build().getActiveTool(TOOL_ID); toolIdAndVersion = TOOL_ID + ":" + LATEST_VERSION; if (toolOpt.isEmpty()) { Assert.fail("Tool '" + toolIdAndVersion + "' is not available in bal-tools.toml"); } // Check for tool availability in local cache Path toolVersionCachePath = toolEnvironments.get(ToolSubCommand.UPDATE).centralCachePath .resolve(Path.of(ORG_NAME, PACKAGE_NAME, LATEST_VERSION)); if (!Files.exists(toolVersionCachePath)) { Assert.fail("Tool '" + toolIdAndVersion + "' is not available in local cache"); } // Execute the tool Pair outputs = executeHelpFlagOfTool( TOOL_ID, toolEnvironments.get(ToolSubCommand.UPDATE).envVariables); Assert.assertEquals(outputs.getLeft(), ""); Assert.assertEquals(outputs.getRight(), readExpectedCmdOutsAsString("tool-execute-specific-help-1.1.0.txt")); } @Test(description = "Update a tool with no new versions", dependsOnMethods = {"testUpdateToolWithNewPatchAndMinor"}, groups = {"update"}) public void testUpdateToolWithNoNewVersions() throws IOException, InterruptedException { // Check for tool availability in bal-tools.toml BalToolsToml balToolsToml = BalToolsToml.from(toolEnvironments.get(ToolSubCommand.UPDATE).balToolsTomlPath); Optional toolOpt = BalToolsManifestBuilder.from(balToolsToml).build() .getActiveTool(TOOL_ID); String toolIdAndVersion = TOOL_ID + ":" + LATEST_VERSION; if (toolOpt.isEmpty()) { Assert.fail("Tool '" + toolIdAndVersion + "' should be the current version in bal-tools.toml"); } // Update the tool Process cmdExec = executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(UPDATE, TOOL_ID)), toolEnvironments.get(ToolSubCommand.UPDATE).envVariables); String cmdErrors = getString(cmdExec.getErrorStream()); if (!cmdErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + cmdErrors); } // Validate the command output String cmdOutput = getString(cmdExec.getInputStream()); String expectedOutput = readExpectedCmdOutsAsString( "tool-update-with-no-new-version.txt"); if (!cmdOutput.contains(expectedOutput)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedOutput + "\nactual output:" + cmdOutput); } // Check for tool availability in bal-tools.toml balToolsToml = BalToolsToml.from(toolEnvironments.get(ToolSubCommand.UPDATE).balToolsTomlPath); toolOpt = BalToolsManifestBuilder.from(balToolsToml).build().getActiveTool(TOOL_ID); toolIdAndVersion = TOOL_ID + ":" + LATEST_VERSION; if (toolOpt.isEmpty()) { Assert.fail("Tool '" + toolIdAndVersion + "' is not available in bal-tools.toml"); } // Execute the tool Pair outputs = executeHelpFlagOfTool( TOOL_ID, toolEnvironments.get(ToolSubCommand.UPDATE).envVariables); Assert.assertEquals(outputs.getLeft(), ""); Assert.assertEquals(outputs.getRight(), readExpectedCmdOutsAsString("tool-execute-specific-help-1.1.0.txt")); } @BeforeGroups(value = "use") public void setupUseTests() throws IOException, InterruptedException { // Pull the latest tool version executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(PULL, TOOL_ID)), toolEnvironments.get(ToolSubCommand.USE).envVariables); // Pull a specific version of a tool String specificToolVersion = TOOL_ID + ":" + SPECIFIC_VERSION; executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(PULL, specificToolVersion)), toolEnvironments.get(ToolSubCommand.USE).envVariables); // Pull tool version with incompatible distribution String incompToolVersion = TOOL_ID + ":" + INCOMPATIBLE_DIST_VERSION; executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(PULL, incompToolVersion)), toolEnvironments.get(ToolSubCommand.USE).envVariables); // Add the dist incompatible version to the toml file BalToolsToml balToolsToml = BalToolsToml.from(toolEnvironments.get(ToolSubCommand.USE).balToolsTomlPath); BalToolsManifest balToolsManifest = BalToolsManifestBuilder.from(balToolsToml).build(); balToolsManifest.addTool(TOOL_ID, ORG_NAME, PACKAGE_NAME, INCOMPATIBLE_DIST_VERSION, false, null); balToolsToml.modify(balToolsManifest); } @Test(description = "Use a newer tool version", groups = {"use"}) public void testUseNewToolVersion() throws IOException, InterruptedException { String toolIdAndVersion = TOOL_ID + ":" + LATEST_VERSION; Process cmdExec = executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(USE, toolIdAndVersion)), toolEnvironments.get(ToolSubCommand.USE).envVariables); String cmdErrors = getString(cmdExec.getErrorStream()); if (!cmdErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + cmdErrors); } // Validate the command output String cmdOutput = getString(cmdExec.getInputStream()); String expectedOutput = readExpectedCmdOutsAsString("tool-use-new-version.txt"); if (!cmdOutput.contains(expectedOutput)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedOutput + "\nactual output:" + cmdOutput); } // Execute the tool Pair outputs = executeHelpFlagOfTool( TOOL_ID, toolEnvironments.get(ToolSubCommand.USE).envVariables); Assert.assertEquals(outputs.getLeft(), ""); Assert.assertEquals(outputs.getRight(), readExpectedCmdOutsAsString("tool-execute-specific-help-1.1.0.txt")); } @Test(description = "Use a newer tool version", dependsOnMethods = {"testUseNewToolVersion"}, groups = {"use"}) public void testUseOldToolVersion() throws IOException, InterruptedException { String toolIdAndVersion = TOOL_ID + ":" + SPECIFIC_VERSION; Process cmdExec = executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(USE, toolIdAndVersion)), toolEnvironments.get(ToolSubCommand.USE).envVariables); String cmdErrors = getString(cmdExec.getErrorStream()); if (!cmdErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + cmdErrors); } // Validate the command output String cmdOutput = getString(cmdExec.getInputStream()); String expectedOutput = readExpectedCmdOutsAsString("tool-use-old-version.txt"); if (!cmdOutput.contains(expectedOutput)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedOutput + "\nactual output:" + cmdOutput); } // Execute the tool Pair outputs = executeHelpFlagOfTool( TOOL_ID, toolEnvironments.get(ToolSubCommand.USE).envVariables); Assert.assertEquals(outputs.getLeft(), ""); Assert.assertEquals(outputs.getRight(), readExpectedCmdOutsAsString("tool-execute-specific-help-1.0.0.txt")); } @Test(description = "Use the currently active tool version", dependsOnMethods = {"testUseOldToolVersion"}, groups = {"use"}) public void testUseCurrentlyActiveToolVersion() throws IOException, InterruptedException { String toolIdAndVersion = TOOL_ID + ":" + SPECIFIC_VERSION; Process cmdExec = executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(USE, toolIdAndVersion)), toolEnvironments.get(ToolSubCommand.USE).envVariables); String cmdErrors = getString(cmdExec.getErrorStream()); if (!cmdErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + cmdErrors); } // Validate the command output String cmdOutput = getString(cmdExec.getInputStream()); String expectedOutput = readExpectedCmdOutsAsString("tool-use-active-version.txt"); if (!cmdOutput.contains(expectedOutput)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedOutput + "\nactual output:" + cmdOutput); } // Execute the tool Pair outputs = executeHelpFlagOfTool( TOOL_ID, toolEnvironments.get(ToolSubCommand.USE).envVariables); Assert.assertEquals(outputs.getLeft(), ""); Assert.assertEquals(outputs.getRight(), readExpectedCmdOutsAsString("tool-execute-specific-help-1.0.0.txt")); } @Test(description = "Use a non existent tool version", dependsOnMethods = {"testUseCurrentlyActiveToolVersion"}, groups = {"use"}) public void testUseNonExistentToolVersion() throws IOException, InterruptedException { String toolIdAndVersion = TOOL_ID + ":" + NON_EXISTING_VERSION; Process cmdExec = executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(USE, toolIdAndVersion)), toolEnvironments.get(ToolSubCommand.USE).envVariables); String cmdErrors = getString(cmdExec.getErrorStream()); String expectedOutput = readExpectedCmdOutsAsString( "tool-use-non-existent-version.txt"); if (!cmdErrors.contains(expectedOutput)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedOutput + "\nactual output:" + cmdErrors); } // Execute the tool Pair outputs = executeHelpFlagOfTool( TOOL_ID, toolEnvironments.get(ToolSubCommand.USE).envVariables); Assert.assertEquals(outputs.getLeft(), ""); Assert.assertEquals(outputs.getRight(), readExpectedCmdOutsAsString("tool-execute-specific-help-1.0.0.txt")); } @Test(description = "Use a tool version built with incompatible distribution", dependsOnMethods = "testUseNonExistentToolVersion", groups = {"use"}) public void testUseToolVersionWithIncompatibleDistribution() throws IOException, InterruptedException { String toolIdAndVersion = TOOL_ID + ":" + INCOMPATIBLE_DIST_VERSION; Process cmdExec = executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(USE, toolIdAndVersion)), toolEnvironments.get(ToolSubCommand.USE).envVariables); String cmdErrors = getString(cmdExec.getErrorStream()); String expectedOutput = readExpectedCmdOutsAsString( "tool-use-with-incompatible-dist.txt"); if (!cmdErrors.contains(expectedOutput)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedOutput + "\nactual output:" + cmdErrors); } // Execute the tool Pair outputs = executeHelpFlagOfTool( TOOL_ID, toolEnvironments.get(ToolSubCommand.USE).envVariables); Assert.assertEquals(outputs.getLeft(), ""); Assert.assertEquals(outputs.getRight(), readExpectedCmdOutsAsString("tool-execute-specific-help-1.0.0.txt")); } @Test(description = "List all tools when there are no tools", groups = {"list"}) public void testListToolsWhenNoToolsInstalled() throws IOException, InterruptedException { // Remove the tool entirely executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(REMOVE, TOOL_ID)), toolEnvironments.get(ToolSubCommand.LIST).envVariables); // List all tools Process cmdExec = executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(List.of(LIST)), toolEnvironments.get(ToolSubCommand.LIST).envVariables); String cmdErrors = getString(cmdExec.getErrorStream()); if (!cmdErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + cmdErrors); } // Validate the command output String cmdOutput = getString(cmdExec.getInputStream()); String expectedOutput = readExpectedCmdOutsAsString("tool-list-with-no-tools.txt"); if (!cmdOutput.contains(expectedOutput)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedOutput + "\nactual output:" + cmdOutput); } } @Test(description = "List all tools when there are multiple versions of tools", groups = {"list"}) public void testListToolsWithMultipleToolVersions() throws IOException, InterruptedException { // Pull the latest tool version executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(PULL, TOOL_ID)), toolEnvironments.get(ToolSubCommand.LIST).envVariables); // Pull a specific version of a tool String specificToolVersion = TOOL_ID + ":" + SPECIFIC_VERSION; executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(PULL, specificToolVersion)), toolEnvironments.get(ToolSubCommand.LIST).envVariables); // List all tools Process cmdExec = executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(List.of(LIST)), toolEnvironments.get(ToolSubCommand.LIST).envVariables); String cmdErrors = getString(cmdExec.getErrorStream()); if (!cmdErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + cmdErrors); } // Validate the command output String cmdOutput = getString(cmdExec.getInputStream()); String expectedOutput = readExpectedCmdOutsAsString( "tool-list-with-multiple-tool-versions.txt"); if (!cmdOutput.contains(expectedOutput)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedOutput + "\nactual output:" + cmdOutput); } } @Test(description = "Search a tool with tool id", groups = {"list"}) public void testSearchAToolWithId() throws IOException, InterruptedException { Process cmdExec = executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(SEARCH, TOOL_ID)), toolEnvironments.get(ToolSubCommand.SEARCH).envVariables); String cmdErrors = getString(cmdExec.getErrorStream()); if (!cmdErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + cmdErrors); } // Validate the command output String cmdOutput = getString(cmdExec.getInputStream()); String expectedOutput = readExpectedCmdOutsAsString("tool-search-with-tool-id.txt"); if (!cmdOutput.contains(expectedOutput)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedOutput + "\nactual output:" + cmdOutput); } } @BeforeGroups(value = "execute_tool") public void setupExecuteToolTests() throws IOException, InterruptedException { // Pull a specific version of a tool String specificToolVersion = TOOL_ID + ":" + SPECIFIC_VERSION; executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(PULL, specificToolVersion)), toolEnvironments.get(ToolSubCommand.EXECUTE).envVariables); // Pull the latest tool version executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList(PULL, TOOL_ID)), toolEnvironments.get(ToolSubCommand.EXECUTE).envVariables); } @Test(description = "Execute bal help with the tool installed", groups = {"execute_tool"}) public void testExecuteGeneralHelpWithToolInstalled() throws IOException, InterruptedException { Process cmdExec = executeHelpCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(), toolEnvironments.get(ToolSubCommand.EXECUTE).envVariables); String cmdErrors = getString(cmdExec.getErrorStream()); if (!cmdErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + cmdErrors); } // Validate the command output String cmdOutput = getString(cmdExec.getInputStream()); String expectedOutput = readExpectedCmdOutsAsString("tool-execute-general-help.txt"); if (!cmdOutput.contains(expectedOutput)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedOutput + "\nactual output:" + cmdOutput); } } @Test(description = "Execute bal help disttest with the tool installed", groups = {"execute_tool"}) public void testExecuteToolSpecificHelpWithToolInstalled() throws IOException, InterruptedException { Process cmdExec = executeHelpCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(List.of(TOOL_ID)), toolEnvironments.get(ToolSubCommand.EXECUTE).envVariables); String cmdErrors = getString(cmdExec.getErrorStream()); if (!cmdErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + cmdErrors); } // Validate the command output String cmdOutput = getString(cmdExec.getInputStream()); String expectedOutput = readExpectedCmdOutsAsString( "tool-execute-specific-help-1.1.0.txt"); if (!cmdOutput.contains(expectedOutput)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedOutput + "\nactual output:" + cmdOutput); } } @Test(description = "Execute disstest tool", groups = {"execute_tool"}) public void testExecuteTool() throws IOException, InterruptedException { Process cmdExec = executeCommand(TOOL_ID, DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(List.of("arg1")), toolEnvironments.get(ToolSubCommand.EXECUTE).envVariables); String cmdErrors = getString(cmdExec.getErrorStream()); if (!cmdErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + cmdErrors); } // Validate the command output String cmdOutput = getString(cmdExec.getInputStream()); String expectedOutput = readExpectedCmdOutsAsString("tool-execute-tool.txt"); if (!cmdOutput.contains(expectedOutput)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedOutput + "\nactual output:" + cmdOutput); } } private Map setToolEnvironmentsForSubCommands() { return Arrays.stream(ToolSubCommand.values()).map( toolSubCommand -> { try { return getToolEnvironment(toolSubCommand); } catch (IOException e) { throw new RuntimeException(e); } }).collect(Collectors.toMap( toolEnvironment -> toolEnvironment.subCommand, toolEnvironment -> toolEnvironment)); } private BalToolTest.ToolEnvironment getToolEnvironment(ToolSubCommand subCommand) throws IOException { Path tempHomeDirectory = Files.createTempDirectory("bal-test-integration-packaging-home-"); Map envVariables = TestUtils.addEnvVariables(getEnvVariables(), tempHomeDirectory); Path balToolsTomlPath = tempHomeDirectory.resolve(".config").resolve("bal-tools.toml"); Path centralCachePath = tempHomeDirectory.resolve( Path.of(REPOSITORIES_DIR, CENTRAL_REPOSITORY_CACHE_NAME, BALA_DIR_NAME)); return new BalToolTest.ToolEnvironment(subCommand, envVariables, balToolsTomlPath, centralCachePath); } private void copyTestResourcesToTempWorkspace() { try { URI testResourcesURI = Objects.requireNonNull(BalToolTest.class.getClassLoader().getResource("bal-tool")). toURI(); Path start = Paths.get(testResourcesURI); Files.walkFileTree(start, new CentralTest.Copy(start, tempWorkspaceDirectory)); } catch (URISyntaxException | IOException e) { Assert.fail("error loading resources"); } } private void pushToolPackages(Map envVariables) throws IOException, InterruptedException { for (String version : Arrays.asList(SPECIFIC_VERSION, PATCH_VERSION, INCOMPATIBLE_DIST_VERSION, LATEST_VERSION)) { Path packagePath = tempWorkspaceDirectory.resolve("v" + version).resolve(PACKAGE_NAME); Process pack = executePackCommand(DISTRIBUTION_FILE_NAME, packagePath, new LinkedList<>(), envVariables); String packErrors = getString(pack.getErrorStream()); if (!packErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + packErrors); } Optional balaPath = getBalaPath(packagePath, version); if (balaPath.isEmpty()) { Assert.fail("Bala file not found for " + TOOL_ID + ":" + version); } // This should make the tools incompatible with the current distribution for incompatible version tests, // and compatible with the current distribution (>=U11) for other versions. String balVersion = version.equals(INCOMPATIBLE_DIST_VERSION) ? "2201.99.0" : "2201.11.0"; updateBallerinaVersionOfBala(balaPath.get(), balVersion); Process build = executePushCommand(DISTRIBUTION_FILE_NAME, packagePath, new LinkedList<>(), envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty() && !buildErrors.contains("already exists")) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } } } private Optional getBalaPath(Path packagePath, String version) { Path balaRootPath = packagePath.resolve("target").resolve("bala"); String balaNamePrefix = ORG_NAME + "-" + PACKAGE_NAME + "-"; String balaNameSuffix = "-" + version + ".bala"; for (JvmTarget jvmTarget : JvmTarget.values()) { Path balaPath = balaRootPath.resolve(balaNamePrefix + jvmTarget.code() + balaNameSuffix); if (Files.exists(balaPath)) { return Optional.of(balaPath); } } return Optional.empty(); } private void updateBallerinaVersionOfBala(Path balaFilePath, String balVersion) throws IOException { Path tempDir = Files.createTempDirectory(balaFilePath.getFileName().toString()); try { unzipBalaFile(balaFilePath, tempDir); updatePackageJsonBallerinaVersion(tempDir, balVersion); zipBalaFile(tempDir, balaFilePath); } catch (IOException e) { Assert.fail("Failed to update the ballerina version of the bala file", e); } } private void unzipBalaFile(Path balaFilePath, Path destDir) throws IOException { File destDirFile = destDir.toFile(); byte[] buffer = new byte[1024]; try (ZipInputStream zis = new ZipInputStream(new FileInputStream(balaFilePath.toFile()))) { ZipEntry zipEntry = zis.getNextEntry(); while (zipEntry != null) { File newFile = newFile(destDirFile, zipEntry); if (zipEntry.isDirectory()) { newFile.mkdirs(); } else { // Create all non-existing directories new File(newFile.getParent()).mkdirs(); try (FileOutputStream fos = new FileOutputStream(newFile)) { int len; while ((len = zis.read(buffer)) > 0) { fos.write(buffer, 0, len); } } } zipEntry = zis.getNextEntry(); } zis.closeEntry(); } } private File newFile(File destinationDir, ZipEntry zipEntry) throws IOException { File destFile = new File(destinationDir, zipEntry.getName()); String destDirPath = destinationDir.getCanonicalPath(); String destFilePath = destFile.getCanonicalPath(); if (!destFilePath.startsWith(destDirPath + File.separator)) { throw new IOException("Entry is outside of the target dir: " + zipEntry.getName()); } return destFile; } private void updatePackageJsonBallerinaVersion(Path packagePath, String balVersion) throws IOException { Path packageJsonPath = packagePath.resolve("package.json"); String content = Files.readString(packageJsonPath); JsonObject jsonObject = JsonParser.parseString(content).getAsJsonObject(); // Update the value for the given key jsonObject.addProperty("ballerina_version", balVersion); Gson gson = new GsonBuilder().setPrettyPrinting().create(); String formattedJson = gson.toJson(jsonObject); Files.writeString(packageJsonPath, formattedJson); } private void zipBalaFile(Path sourceDirPath, Path zipFilePath) throws IOException { Files.deleteIfExists(zipFilePath); Path zipFile = Files.createFile(zipFilePath); try (ZipOutputStream zs = new ZipOutputStream(Files.newOutputStream(zipFile)); Stream walk = Files.walk(sourceDirPath)) { walk.filter(path -> !Files.isDirectory(path)) .forEach(path -> { ZipEntry zipEntry = new ZipEntry(sourceDirPath.relativize(path).toString()); try { zs.putNextEntry(zipEntry); Files.copy(path, zs); zs.closeEntry(); } catch (IOException e) { throw new RuntimeException("\"Failed to re-zip bala with error: ", e); } }); } } private String readExpectedCmdOutsAsString(String outputFileName) { try { return Files.readString(Path.of(Objects.requireNonNull( BalToolTest.class.getClassLoader().getResource( Path.of("bal-tool/cmd-outputs/").resolve(outputFileName).toString())).toURI())); } catch (IOException | URISyntaxException e) { throw new RuntimeException("Error reading resource file"); } } private Pair executeHelpFlagOfTool(String toolId, Map envVariables) throws IOException, InterruptedException { Process cmdExec = executeCommand(toolId, DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(List.of("--help")), envVariables); String cmdErrors = getString(cmdExec.getErrorStream()); String cmdOutput = getString(cmdExec.getInputStream()); return Pair.of(cmdErrors, cmdOutput); } static class ToolEnvironment { ToolSubCommand subCommand; Map envVariables; Path balToolsTomlPath; Path centralCachePath; public ToolEnvironment(ToolSubCommand subCommand, Map envVariables, Path balToolsTomlPath, Path centralCachePath) { this.subCommand = subCommand; this.envVariables = envVariables; this.balToolsTomlPath = balToolsTomlPath; this.centralCachePath = centralCachePath; } } } enum ToolSubCommand { PULL, UPDATE, REMOVE, USE, LIST, SEARCH, EXECUTE } ================================================ FILE: project-api-tests/src/test/java/org/ballerina/projectapi/BuildTimeTest.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.projectapi; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; 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.io.PrintStream; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collections; import java.util.LinkedList; import java.util.Map; import java.util.Objects; import static org.ballerina.projectapi.CentralTestUtils.BALLERINA_HOME_DIR; import static org.ballerina.projectapi.CentralTestUtils.TEST_MODE_ACTIVE; import static org.ballerina.projectapi.CentralTestUtils.createSettingToml; import static org.ballerina.projectapi.CentralTestUtils.deleteFiles; import static org.ballerina.projectapi.CentralTestUtils.getEnvVariables; import static org.ballerina.projectapi.CentralTestUtils.getString; import static org.ballerina.projectapi.TestUtils.DISTRIBUTION_FILE_NAME; import static org.ballerina.projectapi.TestUtils.OUTPUT_CONTAIN_ERRORS; import static org.ballerina.projectapi.TestUtils.executeBuildCommand; /** * Tests related to build time. */ public class BuildTimeTest { private static final PrintStream OUT = System.out; private Path tempHome; private Path tempWorkspace; private Map envVariables; @BeforeClass public void setup() throws IOException { TestUtils.setupDistributions(); tempHome = Files.createTempDirectory("bal-test-integration-packaging-home-"); tempWorkspace = Files.createTempDirectory("bal-test-integration-packaging-workspace-"); createSettingToml(tempHome); envVariables = addEnvVariables(getEnvVariables()); // Copy test resources to temp workspace directory try { URI testResourcesURI = Objects.requireNonNull(getClass().getClassLoader().getResource("build-time")). toURI(); Files.walkFileTree(Paths.get(testResourcesURI), new CentralTest.Copy(Paths.get(testResourcesURI), this.tempWorkspace)); } catch (URISyntaxException e) { Assert.fail("error loading resources"); } } @Test(enabled = false, description = "Build project twice to verify reduction in package resolution time due to caching.") public void testResolutionTimeReduction() throws IOException, InterruptedException { String projectName = "Project1"; long expectedMinimumTimeDiff = 500; long firstResolutionTime = getPackageResolutionTime(projectName); OUT.println("Package resolution time for the first attempt: " + firstResolutionTime); long consecutiveResolutionTime = getPackageResolutionTime(projectName); OUT.println("Package resolution time for the consecutive attempt: " + consecutiveResolutionTime); // Assert whether the resolution time has reduced more than the expected minimum time difference Assert.assertTrue((firstResolutionTime - consecutiveResolutionTime) > expectedMinimumTimeDiff); } @Test(description = "Verify the offline resolution during code generation.") public void testCodeGenOfflineResolution() throws IOException, InterruptedException { String projectWithoutTest = "Project2"; String projectWithTest = "Project3"; long firstBuildTime = getPackageBuildTime(projectWithoutTest); OUT.println("Package build time for the first attempt without tests: " + firstBuildTime); long secondBuildTime = getPackageBuildTime(projectWithTest); OUT.println("Package build time for the consecutive attempt with tests: " + secondBuildTime); // Assert whether the resolution time has reduced more than the expected minimum time difference Assert.assertTrue(firstBuildTime * 2 > secondBuildTime); } /** * Get environment variables and add ballerina_home as a env variable the tmp directory. * * @return env directory variable array */ private Map addEnvVariables(Map envVariables) { envVariables.put(BALLERINA_HOME_DIR, tempHome.toString()); envVariables.put(TEST_MODE_ACTIVE, "true"); return envVariables; } /** * Given the project name get the package resolution time in milliseconds. * * @param projectName ballerina project name * @return package resolution time in milliseconds as a long * @throws IOException * @throws InterruptedException */ private long getPackageResolutionTime(String projectName) throws IOException, InterruptedException { String targetDir = "target"; String buildTimeFileName = "build-time.json"; String buildDumpFlag = "--dump-build-time"; String packageResolutionField = "packageResolutionDuration"; Process build = executeBuildCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspace.resolve(projectName), new LinkedList<>(Collections.singletonList(buildDumpFlag)), this.envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } Path buildTimeJson = this.tempWorkspace.resolve(projectName).resolve(targetDir).resolve(buildTimeFileName); if (buildTimeJson.toFile().exists()) { JsonNode jsonTree = new ObjectMapper().readTree(buildTimeJson.toFile()); return jsonTree.get(packageResolutionField).asLong(); } else { Assert.fail("Unable to locate the JSON file with build time at " + buildTimeJson.toString()); } // Return 0 if there is a failure. return 0; } /** * Given the project name get the package build time in milliseconds. * * @param projectName ballerina project name * @return package resolution time in milliseconds as a long * @throws IOException * @throws InterruptedException */ private long getPackageBuildTime(String projectName) throws IOException, InterruptedException { String targetDir = "target"; String buildTimeFileName = "build-time.json"; String buildDumpFlag = "--dump-build-time"; String totalDuration = "totalDuration"; Process build = executeBuildCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspace.resolve(projectName), new LinkedList<>(Collections.singletonList(buildDumpFlag)), this.envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } Path buildTimeJson = this.tempWorkspace.resolve(projectName).resolve(targetDir).resolve(buildTimeFileName); if (buildTimeJson.toFile().exists()) { JsonNode jsonTree = new ObjectMapper().readTree(buildTimeJson.toFile()); return jsonTree.get(totalDuration).asLong(); } else { Assert.fail("Unable to locate the JSON file with build time at " + buildTimeJson.toString()); } // Return 0 if there is a failure. return 0; } @AfterClass private void cleanup() throws IOException { deleteFiles(tempHome); deleteFiles(tempWorkspace); } } ================================================ FILE: project-api-tests/src/test/java/org/ballerina/projectapi/CentralNegativeTest.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.projectapi; 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.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collections; import java.util.LinkedList; import java.util.Map; import java.util.Objects; import static org.ballerina.projectapi.CentralTestUtils.BALLERINA_CENTRAL_ACCESS_TOKEN; import static org.ballerina.projectapi.CentralTestUtils.BALLERINA_DEV_CENTRAL; import static org.ballerina.projectapi.CentralTestUtils.BALLERINA_HOME_DIR; import static org.ballerina.projectapi.CentralTestUtils.BALLERINA_TOML; import static org.ballerina.projectapi.CentralTestUtils.deleteFiles; import static org.ballerina.projectapi.CentralTestUtils.getBalaPath; import static org.ballerina.projectapi.CentralTestUtils.getEnvVariables; import static org.ballerina.projectapi.CentralTestUtils.getGenerateBalaLog; import static org.ballerina.projectapi.CentralTestUtils.getString; import static org.ballerina.projectapi.CentralTestUtils.randomPackageName; import static org.ballerina.projectapi.CentralTestUtils.updateFileToken; import static org.ballerina.projectapi.TestUtils.CODE_NAME; import static org.ballerina.projectapi.TestUtils.DISTRIBUTIONS_DIR; import static org.ballerina.projectapi.TestUtils.MAVEN_VERSION; import static org.ballerina.projectapi.TestUtils.OUTPUT_CONTAIN_ERRORS; import static org.ballerina.projectapi.TestUtils.executePackCommand; import static org.ballerina.projectapi.TestUtils.executePushCommand; import static org.ballerina.projectapi.TestUtils.executeSearchCommand; /** * Negative tests related central packaging. */ public class CentralNegativeTest { private Path tempHomeDirectory; private Path tempWorkspaceDirectory; private String packageAName; private Map envVariables; private static final String ORG_NAME = "bctestorg"; private static final String DISTRIBUTION_FILE_NAME = "ballerina-" + MAVEN_VERSION + "-" + CODE_NAME; private static final String DEFAULT_PKG_NAME = "my_package"; private static final String PROJECT_A = "projectA"; private static final String COMMON_VERSION = "1.0.0"; private static final String TEST_PREFIX = "test_"; private static final String ANY_PLATFORM = "any"; private static final String OUTPUT_NOT_CONTAINS_EXP_MSG = "build output does not contain expected message:"; @BeforeClass() public void setUp() throws IOException, InterruptedException { setupDistributions(); tempHomeDirectory = Files.createTempDirectory("bal-test-integration-packaging-home-"); tempWorkspaceDirectory = Files.createTempDirectory("bal-test-integration-packaging-workspace-"); envVariables = getEnvVariables(); // Copy test resources to temp workspace directory try { URI testResourcesURI = Objects.requireNonNull(getClass().getClassLoader().getResource("central")).toURI(); Files.walkFileTree(Paths.get(testResourcesURI), new CentralTest.Copy(Paths.get(testResourcesURI), this.tempWorkspaceDirectory)); } catch (URISyntaxException e) { Assert.fail("error loading resources"); } // Get random package names do { String randomString = randomPackageName(10); this.packageAName = TEST_PREFIX + randomString + "_" + PROJECT_A; } while (isPkgAvailableInCentral(this.packageAName)); isPkgAvailableInCentral(this.packageAName); // Update Ballerina.toml files with new package name "my_package" updateFileToken(this.tempWorkspaceDirectory.resolve(PROJECT_A).resolve(BALLERINA_TOML), DEFAULT_PKG_NAME, this.packageAName); } @Test(description = "Build and push package with invalid access token", enabled = false) public void testPushPackageWithInvalidAccessToken() throws IOException, InterruptedException { Map envVariablesWithInvalidAccessToken = addEnvVariablesWithInvalidAccessToken( this.envVariables); Process build = executePackCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve(PROJECT_A), new LinkedList<>(), envVariablesWithInvalidAccessToken); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(build.getInputStream()); if (!buildOutput.contains(getGenerateBalaLog(ORG_NAME, this.packageAName, ANY_PLATFORM, COMMON_VERSION))) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + getGenerateBalaLog(ORG_NAME, this.packageAName, ANY_PLATFORM, COMMON_VERSION)); } Assert.assertTrue( getBalaPath(this.tempWorkspaceDirectory.resolve(PROJECT_A), ORG_NAME, this.packageAName, ANY_PLATFORM, COMMON_VERSION).toFile().exists()); Process push = executePushCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve(PROJECT_A), new LinkedList<>(), envVariablesWithInvalidAccessToken); String pushErrors = getString(push.getErrorStream()); if (pushErrors.isEmpty()) { Assert.fail("push command output does not contain any error"); } String expectedError = "ballerina: unauthorized access token for organization: 'bctestorg'. " + "reason: unauthorized to perform this operation as access token is invalid. " + "check access token set in 'Settings.toml' file."; Assert.assertEquals(pushErrors, expectedError); } @AfterClass private void cleanup() throws IOException { deleteFiles(tempHomeDirectory); deleteFiles(tempWorkspaceDirectory); } private Map addEnvVariablesWithInvalidAccessToken(Map envVariables) { envVariables.put(BALLERINA_HOME_DIR, tempHomeDirectory.toString()); envVariables.put(BALLERINA_DEV_CENTRAL, "true"); envVariables.put(BALLERINA_CENTRAL_ACCESS_TOKEN, "cedbeff5-some-invalid-token-f38d4abf"); return envVariables; } /** * Clean and set up distributions. */ private void setupDistributions() throws IOException { TestUtils.cleanDistribution(); TestUtils.prepareDistribution(DISTRIBUTIONS_DIR.resolve(DISTRIBUTION_FILE_NAME + ".zip")); } /** * Check package already available in central. * * @param pkg package * @return is package available in central */ private boolean isPkgAvailableInCentral(String pkg) throws IOException, InterruptedException { Process search = executeSearchCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory, new LinkedList<>(Collections.singletonList(pkg)), this.envVariables); String buildErrors = getString(search.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(search.getInputStream()); return !buildOutput.contains("no modules found"); } } ================================================ FILE: project-api-tests/src/test/java/org/ballerina/projectapi/CentralTest.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.projectapi; 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.net.URI; import java.net.URISyntaxException; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.StandardCopyOption; import java.nio.file.attribute.BasicFileAttributes; import java.util.Collections; import java.util.LinkedList; import java.util.Map; import java.util.Objects; import static org.ballerina.projectapi.CentralTestUtils.ANY_PLATFORM; import static org.ballerina.projectapi.CentralTestUtils.BALLERINA_TOML; import static org.ballerina.projectapi.CentralTestUtils.COMMON_VERSION; import static org.ballerina.projectapi.CentralTestUtils.JAVA_PLATFORM; import static org.ballerina.projectapi.CentralTestUtils.MAIN_BAL; import static org.ballerina.projectapi.CentralTestUtils.OUTPUT_NOT_CONTAINS_EXP_MSG; import static org.ballerina.projectapi.CentralTestUtils.PULLED_FROM_CENTRAL_MSG; import static org.ballerina.projectapi.CentralTestUtils.TEST_PREFIX; import static org.ballerina.projectapi.CentralTestUtils.createSettingToml; import static org.ballerina.projectapi.CentralTestUtils.deleteBalaOfPackage; import static org.ballerina.projectapi.CentralTestUtils.deleteFiles; import static org.ballerina.projectapi.CentralTestUtils.getBalaPath; import static org.ballerina.projectapi.CentralTestUtils.getEnvVariables; import static org.ballerina.projectapi.CentralTestUtils.getGenerateBalaLog; import static org.ballerina.projectapi.CentralTestUtils.getPushedToCentralLog; import static org.ballerina.projectapi.CentralTestUtils.getString; import static org.ballerina.projectapi.CentralTestUtils.isPkgAvailableInCentral; import static org.ballerina.projectapi.CentralTestUtils.randomPackageName; import static org.ballerina.projectapi.CentralTestUtils.updateFileToken; import static org.ballerina.projectapi.TestUtils.DISTRIBUTION_FILE_NAME; import static org.ballerina.projectapi.TestUtils.OUTPUT_CONTAIN_ERRORS; import static org.ballerina.projectapi.TestUtils.executePackCommand; import static org.ballerina.projectapi.TestUtils.executePullCommand; import static org.ballerina.projectapi.TestUtils.executePushCommand; /** * Tests related central packaging. */ public class CentralTest { private Path tempHomeDirectory; private Path tempWorkspaceDirectory; private String packageAName; private String packageBName; private String packageCName; private String packageDName; private String packageEName; private String packageFName; private String packageGName; private String packageSnapshotName; private final String orgName = "bctestorg"; private Map envVariables; private static final String DEFAULT_PKG_NAME = "my_package"; private static final String PROJECT_A = "projectA"; private static final String PROJECT_B = "projectB"; private static final String PROJECT_C = "projectC"; private static final String PROJECT_D = "projectD"; private static final String PROJECT_E = "projectE"; private static final String PROJECT_F = "projectF"; private static final String PROJECT_G = "projectG"; private static final String PROJECT_SNAPSHOT = "projectSnapshot"; @BeforeClass() public void setUp() throws IOException, InterruptedException { TestUtils.setupDistributions(); tempHomeDirectory = Files.createTempDirectory("bal-test-integration-packaging-home-"); tempWorkspaceDirectory = Files.createTempDirectory("bal-test-integration-packaging-workspace-"); createSettingToml(tempHomeDirectory); envVariables = TestUtils.addEnvVariables(getEnvVariables(), tempHomeDirectory); // Copy test resources to temp workspace directory try { URI testResourcesURI = Objects.requireNonNull(getClass().getClassLoader().getResource("central")).toURI(); Files.walkFileTree(Paths.get(testResourcesURI), new CentralTest.Copy(Paths.get(testResourcesURI), this.tempWorkspaceDirectory)); } catch (URISyntaxException e) { Assert.fail("error loading resources"); } // Get random package names do { String randomString = randomPackageName(10); this.packageAName = TEST_PREFIX + randomString + "_" + PROJECT_A; this.packageBName = TEST_PREFIX + randomString + "_" + PROJECT_B; this.packageCName = TEST_PREFIX + randomString + "_" + PROJECT_C; this.packageDName = TEST_PREFIX + randomString + "_" + PROJECT_D; this.packageEName = TEST_PREFIX + randomString + "_" + PROJECT_E; this.packageFName = TEST_PREFIX + randomString + "_" + PROJECT_F; this.packageGName = TEST_PREFIX + randomString + "_" + PROJECT_G; this.packageSnapshotName = TEST_PREFIX + randomString + "_" + PROJECT_SNAPSHOT; } while (isPkgAvailableInCentral(this.packageAName, tempWorkspaceDirectory, envVariables) || isPkgAvailableInCentral(this.packageBName, tempWorkspaceDirectory, envVariables) || isPkgAvailableInCentral(this.packageCName, tempWorkspaceDirectory, envVariables) || isPkgAvailableInCentral(this.packageDName, tempWorkspaceDirectory, envVariables) || isPkgAvailableInCentral(this.packageEName, tempWorkspaceDirectory, envVariables) || isPkgAvailableInCentral(this.packageFName, tempWorkspaceDirectory, envVariables) || isPkgAvailableInCentral(this.packageGName, tempWorkspaceDirectory, envVariables) || isPkgAvailableInCentral(this.packageSnapshotName, tempWorkspaceDirectory, envVariables)); isPkgAvailableInCentral(this.packageAName, tempWorkspaceDirectory, envVariables); // Update Ballerina.toml files with new package names"my_package" updateFileToken(this.tempWorkspaceDirectory.resolve(PROJECT_A).resolve(BALLERINA_TOML), DEFAULT_PKG_NAME, this.packageAName); updateFileToken(this.tempWorkspaceDirectory.resolve(PROJECT_B).resolve(BALLERINA_TOML), DEFAULT_PKG_NAME, this.packageBName); updateFileToken(this.tempWorkspaceDirectory.resolve(PROJECT_C).resolve(BALLERINA_TOML), DEFAULT_PKG_NAME, this.packageCName); updateFileToken(this.tempWorkspaceDirectory.resolve(PROJECT_D).resolve(BALLERINA_TOML), DEFAULT_PKG_NAME, this.packageDName); updateFileToken(this.tempWorkspaceDirectory.resolve(PROJECT_E).resolve(BALLERINA_TOML), DEFAULT_PKG_NAME, this.packageEName); updateFileToken(this.tempWorkspaceDirectory.resolve(PROJECT_F).resolve(BALLERINA_TOML), DEFAULT_PKG_NAME, this.packageFName); updateFileToken(this.tempWorkspaceDirectory.resolve(PROJECT_G).resolve(BALLERINA_TOML), DEFAULT_PKG_NAME, this.packageGName); updateFileToken(this.tempWorkspaceDirectory.resolve(PROJECT_SNAPSHOT).resolve(BALLERINA_TOML), DEFAULT_PKG_NAME, this.packageSnapshotName); // Update imports updateFileToken(this.tempWorkspaceDirectory.resolve(PROJECT_C).resolve(MAIN_BAL), "", this.packageAName); updateFileToken(this.tempWorkspaceDirectory.resolve(PROJECT_C).resolve(MAIN_BAL), "", this.packageBName); updateFileToken(this.tempWorkspaceDirectory.resolve(PROJECT_D).resolve(MAIN_BAL), "", this.packageCName); updateFileToken(this.tempWorkspaceDirectory.resolve(PROJECT_F).resolve(MAIN_BAL), "", this.packageEName); updateFileToken(this.tempWorkspaceDirectory.resolve(PROJECT_G).resolve(MAIN_BAL), "", this.packageFName); } @Test(description = "Build package A with a native lib dependency") public void testBuildPackageA() throws IOException, InterruptedException { Process build = executePackCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve(PROJECT_A), new LinkedList<>(), this.envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(build.getInputStream()); if (!buildOutput.contains(getGenerateBalaLog(orgName, this.packageAName, ANY_PLATFORM, COMMON_VERSION))) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + getGenerateBalaLog(orgName, this.packageAName, ANY_PLATFORM, COMMON_VERSION)); } Assert.assertTrue( getBalaPath(this.tempWorkspaceDirectory.resolve(PROJECT_A), orgName, this.packageAName, ANY_PLATFORM, COMMON_VERSION).toFile().exists()); } @Test(description = "Push package A to central", dependsOnMethods = "testBuildPackageA") public void testPushPackageA() throws IOException, InterruptedException { Process build = executePushCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve(PROJECT_A), new LinkedList<>(), this.envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(build.getInputStream()); if (!buildOutput.contains(getPushedToCentralLog(orgName, packageAName))) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + getPushedToCentralLog(orgName, packageAName)); } } @Test(description = "Build package B which has java21 platform dependency") public void testBuildPackageB() throws IOException, InterruptedException { Process build = executePackCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve(PROJECT_B), new LinkedList<>(), this.envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(build.getInputStream()); if (!buildOutput.contains(getGenerateBalaLog(orgName, this.packageBName, JAVA_PLATFORM, COMMON_VERSION))) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + getGenerateBalaLog(orgName, this.packageBName, JAVA_PLATFORM, COMMON_VERSION)); } Assert.assertTrue( getBalaPath(this.tempWorkspaceDirectory.resolve(PROJECT_B), orgName, this.packageBName, JAVA_PLATFORM, COMMON_VERSION).toFile().exists()); } @Test(description = "Build package C which depends on Package A and B", dependsOnMethods = { "testPushPackageA", "testBuildPackageB" }) public void testBuildPackageC() throws IOException, InterruptedException { String expectedMsg = "cannot resolve module '" + orgName + "/" + this.packageBName + " as pkgB'"; String unexpectedMsg = "cannot resolve module '" + orgName + "/" + this.packageAName + " as pkgA'"; Process build = executePackCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve(PROJECT_C), new LinkedList<>(), this.envVariables); String buildErrors = getString(build.getErrorStream()); if (buildErrors.isEmpty()) { Assert.fail("build output should contain errors."); } if (buildErrors.contains(unexpectedMsg)) { Assert.fail("build output should not contain unexpected message:" + unexpectedMsg); } if (!buildErrors.contains(expectedMsg)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedMsg); } } @Test(description = "Push package B to central", dependsOnMethods = "testBuildPackageC") public void testPushPackageB() throws IOException, InterruptedException { Process build = executePushCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve(PROJECT_B), new LinkedList<>(), this.envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(build.getInputStream()); if (!buildOutput.contains(getPushedToCentralLog(orgName, this.packageBName))) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + getPushedToCentralLog(orgName, this.packageBName)); } } @Test(description = "Build package C after pushing Package B", dependsOnMethods = "testPushPackageB") public void testBuildPackageCAgain() throws IOException, InterruptedException { Process build = executePackCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve(PROJECT_C), new LinkedList<>(), this.envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(build.getInputStream()); if (!buildOutput.contains(getGenerateBalaLog(orgName, this.packageCName, ANY_PLATFORM, COMMON_VERSION))) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + getGenerateBalaLog(orgName, this.packageCName, ANY_PLATFORM, COMMON_VERSION)); } Assert.assertTrue( getBalaPath(this.tempWorkspaceDirectory.resolve(PROJECT_C), orgName, this.packageCName, ANY_PLATFORM, COMMON_VERSION).toFile().exists()); } @Test(description = "Push package C to central", dependsOnMethods = "testBuildPackageCAgain") public void testPushPackageC() throws IOException, InterruptedException { Process build = executePushCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve(PROJECT_C), new LinkedList<>(), this.envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(build.getInputStream()); if (!buildOutput.contains(getPushedToCentralLog(orgName, this.packageCName))) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + getPushedToCentralLog(orgName, this.packageCName)); } } @Test(description = "Build package with pre-release version") public void testBuildSnapshotPackage() throws IOException, InterruptedException { String snapshotVersion = "1.0.0-snapshot"; Process build = executePackCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve(PROJECT_SNAPSHOT), new LinkedList<>(), this.envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(build.getInputStream()); if (!buildOutput.contains(getGenerateBalaLog( orgName, this.packageSnapshotName, ANY_PLATFORM, snapshotVersion))) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + getGenerateBalaLog(orgName, this.packageSnapshotName, ANY_PLATFORM, snapshotVersion)); } Assert.assertTrue( getBalaPath(this.tempWorkspaceDirectory.resolve(PROJECT_SNAPSHOT), orgName, this.packageSnapshotName, ANY_PLATFORM, snapshotVersion).toFile().exists()); } @Test(description = "Push package with pre-release version to central", dependsOnMethods = "testBuildSnapshotPackage") public void testPushSnapshotPackage() throws IOException, InterruptedException { Process build = executePushCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve(PROJECT_SNAPSHOT), new LinkedList<>(), this.envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(build.getInputStream()); String expectedMsg = orgName + "/" + this.packageSnapshotName + ":1.0.0-snapshot pushed to central successfully"; if (!buildOutput.contains(expectedMsg)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedMsg); } } @Test(description = "Pull package with pre-release version from central", dependsOnMethods = "testPushSnapshotPackage") public void testPullSnapshotPackage() throws IOException, InterruptedException { String pkg = orgName + "/" + this.packageSnapshotName + ":1.0.0-snapshot"; Process build = executePullCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve(PROJECT_SNAPSHOT), new LinkedList<>(Collections.singletonList(pkg)), this.envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(build.getInputStream()); String expectedMsg = pkg + " pulled from central successfully"; if (!buildOutput.contains(expectedMsg)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedMsg); } } @Test(description = "Build package E") public void testBuildPackageE() throws IOException, InterruptedException { Process build = executePackCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve(PROJECT_E), new LinkedList<>(), this.envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(build.getInputStream()); if (!buildOutput.contains(getGenerateBalaLog(orgName, this.packageEName, ANY_PLATFORM, COMMON_VERSION))) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + getGenerateBalaLog(orgName, this.packageEName, ANY_PLATFORM, COMMON_VERSION)); } Assert.assertTrue( getBalaPath(this.tempWorkspaceDirectory.resolve(PROJECT_E), orgName, this.packageEName, ANY_PLATFORM, COMMON_VERSION).toFile().exists()); } @Test(description = "Push package E to the central", dependsOnMethods = "testBuildPackageE") public void testPushPackageE() throws IOException, InterruptedException { Process push = executePushCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve(PROJECT_E), new LinkedList<>(), this.envVariables); String pushErrors = getString(push.getErrorStream()); if (!pushErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + pushErrors); } String pushOutput = getString(push.getInputStream()); if (!pushOutput.contains(getPushedToCentralLog(orgName, this.packageEName))) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + getPushedToCentralLog(orgName, this.packageEName)); } } @Test(description = "Build package F", dependsOnMethods = "testPushPackageE") public void testBuildPackageF() throws IOException, InterruptedException { Process build = executePackCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve(PROJECT_F), new LinkedList<>(), this.envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(build.getInputStream()); if (!buildOutput.contains(getGenerateBalaLog(orgName, this.packageFName, ANY_PLATFORM, COMMON_VERSION))) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + getGenerateBalaLog(orgName, this.packageFName, ANY_PLATFORM, COMMON_VERSION)); } Assert.assertTrue( getBalaPath(this.tempWorkspaceDirectory.resolve(PROJECT_F), orgName, this.packageFName, ANY_PLATFORM, COMMON_VERSION).toFile().exists()); } @Test(description = "Push package F to the central", dependsOnMethods = "testBuildPackageF") public void testPushPackageF() throws IOException, InterruptedException { Process push = executePushCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve(PROJECT_F), new LinkedList<>(), this.envVariables); String pushErrors = getString(push.getErrorStream()); if (!pushErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + pushErrors); } String pushOutput = getString(push.getInputStream()); if (!pushOutput.contains(getPushedToCentralLog(orgName, this.packageFName))) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + getPushedToCentralLog(orgName, this.packageFName)); } } @Test(description = "Build package G", dependsOnMethods = "testPushPackageF") public void testBuildPackageG() throws IOException, InterruptedException { Process build = executePackCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve(PROJECT_G), new LinkedList<>(), this.envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(build.getInputStream()); if (!buildOutput.contains(getGenerateBalaLog(orgName, this.packageGName, ANY_PLATFORM, COMMON_VERSION))) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + getGenerateBalaLog(orgName, this.packageGName, ANY_PLATFORM, COMMON_VERSION)); } Assert.assertTrue( getBalaPath(this.tempWorkspaceDirectory.resolve(PROJECT_G), orgName, this.packageGName, ANY_PLATFORM, COMMON_VERSION).toFile().exists()); } @Test(description = "Push package G", dependsOnMethods = "testBuildPackageG") public void testPushPackageG() throws IOException, InterruptedException { Process push = executePushCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve(PROJECT_G), new LinkedList<>(), this.envVariables); String pushErrors = getString(push.getErrorStream()); if (!pushErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + pushErrors); } String pushOutput = getString(push.getInputStream()); if (!pushOutput.contains(getPushedToCentralLog(orgName, this.packageGName))) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + getPushedToCentralLog(orgName, this.packageGName)); } } @Test(description = "Pull package G from central. Should pull both E, F since they are dependencies of G.", dependsOnMethods = "testPushPackageG") public void testPullPackageG() throws IOException, InterruptedException { deleteBalaOfPackage(orgName, packageEName); deleteBalaOfPackage(orgName, packageFName); deleteBalaOfPackage(orgName, packageGName); String pkgE = orgName + "/" + this.packageEName + ":1.0.0"; String pkgF = orgName + "/" + this.packageFName + ":1.0.0"; String pkgG = orgName + "/" + this.packageGName + ":1.0.0"; Process pull = executePullCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve(PROJECT_G), new LinkedList<>(Collections.singletonList(pkgG)), this.envVariables); String buildErrors = getString(pull.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(pull.getInputStream()); String expectedMsg1 = pkgE + PULLED_FROM_CENTRAL_MSG; String expectedMsg2 = pkgF + PULLED_FROM_CENTRAL_MSG; String expectedMsg3 = pkgG + PULLED_FROM_CENTRAL_MSG; if (!buildOutput.contains(expectedMsg1)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedMsg1); } if (!buildOutput.contains(expectedMsg2)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedMsg2); } if (!buildOutput.contains(expectedMsg3)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedMsg3); } } @AfterClass private void cleanup() throws IOException { deleteFiles(tempHomeDirectory); deleteFiles(tempWorkspaceDirectory); } /** * Copy test resources to temp directory. */ static class Copy extends SimpleFileVisitor { private final Path fromPath; private final Path toPath; private final StandardCopyOption copyOption; Copy(Path fromPath, Path toPath, StandardCopyOption copyOption) { this.fromPath = fromPath; this.toPath = toPath; this.copyOption = copyOption; } Copy(Path fromPath, Path toPath) { this(fromPath, toPath, StandardCopyOption.REPLACE_EXISTING); } @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { Path targetPath = toPath.resolve(fromPath.relativize(dir).toString()); if (!Files.exists(targetPath)) { Files.createDirectory(targetPath); } return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { Files.copy(file, toPath.resolve(fromPath.relativize(file).toString()), copyOption); return FileVisitResult.CONTINUE; } } } ================================================ FILE: project-api-tests/src/test/java/org/ballerina/projectapi/CentralTestUtils.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.projectapi; import io.ballerina.projects.util.ProjectConstants; import io.ballerina.projects.util.ProjectUtils; import org.testng.Assert; import java.io.BufferedReader; import java.io.FileReader; import java.io.FileWriter; 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.StandardOpenOption; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; import static org.ballerina.projectapi.TestUtils.DISTRIBUTION_FILE_NAME; import static org.ballerina.projectapi.TestUtils.OUTPUT_CONTAIN_ERRORS; import static org.ballerina.projectapi.TestUtils.executePackCommand; import static org.ballerina.projectapi.TestUtils.executePushCommand; import static org.ballerina.projectapi.TestUtils.executeSearchCommand; import static org.ballerina.projectapi.TestUtils.executeToolCommand; /** * Utility class for central tests. */ public class CentralTestUtils { private CentralTestUtils() { } static final String BALLERINA_HOME_DIR = "BALLERINA_HOME_DIR"; static final String BALLERINA_DEV_CENTRAL = "BALLERINA_DEV_CENTRAL"; static final String TEST_MODE_ACTIVE = "TEST_MODE_ACTIVE"; static final String BALLERINA_CENTRAL_ACCESS_TOKEN = "BALLERINA_CENTRAL_ACCESS_TOKEN"; static final String BALLERINA_TOML = "Ballerina.toml"; static final String DEPENDENCIES_TOML = "Dependencies.toml"; static final String BAL_TOOL_TOML = "BalTool.toml"; static final String MAIN_BAL = "main.bal"; static final String COMMON_VERSION = "1.0.0"; static final String TEST_PREFIX = "test_"; static final String ANY_PLATFORM = "any"; static final String JAVA_PLATFORM = "java21"; static final String BALLERINA_ARTIFACT_TYPE = "bala"; static final String OUTPUT_NOT_CONTAINS_EXP_MSG = "build output does not contain expected message:"; static final String PULLED_FROM_CENTRAL_MSG = " pulled from central successfully"; static final String PACKAGE_NAME_SEPARATOR = "_"; static final String PACKAGE_PATH_SEPARATOR = "/"; /** * Generate random package name. * * @param count number of characters required * @return generated name */ static String randomPackageName(int count) { String upperCaseAlpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; String lowerCaseAlpha = "abcdefghijklmnopqrstuvwxyz"; String alpha = upperCaseAlpha + lowerCaseAlpha; StringBuilder builder = new StringBuilder(); while (count-- != 0) { int character = (int) (Math.random() * alpha.length()); builder.append(alpha.charAt(character)); } return builder.toString(); } /** * Create Settings.toml inside the home repository. * * @throws IOException i/o exception when writing to file */ static void createSettingToml(Path dirPath) throws IOException { String content = "[central]\n accesstoken = \"" + getProdToken() + "\""; Files.write(dirPath.resolve("Settings.toml"), content.getBytes(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); } /** * Get token of ballerina-dev-central required to push the module. * * @return token required to push the module. */ private static String getDevToken() { // staging and dev both has the same access token return System.getenv("devCentralToken"); } /** * Get token of ballerina-central required to push the module. * * @return token required to push the module. */ private static String getProdToken() { return System.getenv("prodCentralToken"); } /** * Get token of ballerina-bot required to dispatch GitHub workflows. * * @return token required to dispatch GitHub workflows. */ public static String getBallerinaBotWorkflow() { return System.getenv("ballerinaBotWorkflow"); } /** * Get environment variables and add ballerina_home as a env variable the tmp directory. * * @return env directory variable array */ static Map getEnvVariables() { Map envVarMap = System.getenv(); Map retMap = new HashMap<>(); envVarMap.forEach(retMap::put); return retMap; } /** * Update the file token by changing the guess token. * * @param filePath The file path. * @param guessToken The guess token. * @param actualToken The guess token. * @throws IOException Error when writing to Ballerina.toml */ static void updateFileToken(Path filePath, String guessToken, String actualToken) throws IOException { Stream lines = Files.lines(filePath); List replaced = lines.map(line -> line.replaceAll(guessToken, actualToken)) .collect(Collectors.toList()); Files.write(filePath, replaced); lines.close(); } /** * Convert input stream to string. * * @param outputs input stream * @return converted string * @throws IOException Error when reading from input stream */ static String getString(InputStream outputs) throws IOException { try (BufferedReader br = new BufferedReader(new InputStreamReader(outputs))) { Stream logLines = br.lines(); String generatedLog = logLines.collect(Collectors.joining("\n")); logLines.close(); return generatedLog; } } /** * Delete files inside directories. * * @param dirPath directory path * @throws IOException throw an exception if an issue occurs */ static void deleteFiles(Path dirPath) throws IOException { if (dirPath == null) { return; } Files.walk(dirPath).sorted(Comparator.reverseOrder()).forEach(path -> { try { Files.delete(path); } catch (IOException e) { Assert.fail(e.getMessage(), e); } }); } /** * Get generate bala log. * * @param org org name * @param pkgName package name * @param platform platform name * @param version package version * @return generate bala log */ static String getGenerateBalaLog(String org, String pkgName, String platform, String version) { return "Creating bala\n" + "\ttarget/bala/" + org + "-" + pkgName + "-" + platform + "-" + version + ".bala"; } /** * Get the log output when package is successfully pushed to Central. * * @param orgName Organization name * @param pkgName Name of the package pushed to central * @param version Package Version * @return Expected log message */ static String getPushedToCentralLog(String orgName, String pkgName, String version) { return orgName + "/" + pkgName + ":" + version + " pushed to central successfully"; } /** * Get pushed to central log. * * @param orgName Organization name * @param pkgName package name * @return pushed to central log */ static String getPushedToCentralLog(String orgName, String pkgName) { return orgName + "/" + pkgName + ":1.0.0 pushed to central successfully"; } /** * Get executable jar path. * * @param projectPath project path * @param pkgName package name * @return executable jar path */ static Path getExecutableJarPath(Path projectPath, String pkgName) { return projectPath.resolve("target").resolve("bin").resolve(pkgName + ".jar"); } /** * Get executable jar path. * * @param projectPath project path * @param org org name * @param pkgName package name * @param platform platform name * @param version package version * @return bala path */ static Path getBalaPath(Path projectPath, String org, String pkgName, String platform, String version) { return projectPath.resolve("target").resolve(BALLERINA_ARTIFACT_TYPE) .resolve(org + "-" + pkgName + "-" + platform + "-" + version + ".bala"); } /** * Check if package already available on central. * * @param pkg package * @param tempWorkspaceDirectory Path to workspace * @param envVariables Environmental variables * @return whether the package is available on central * @throws IOException * @throws InterruptedException */ static boolean isPkgAvailableInCentral(String pkg, Path tempWorkspaceDirectory, Map envVariables) throws IOException, InterruptedException { String buildOutput = searchPackageDetails(pkg, tempWorkspaceDirectory, envVariables); return !buildOutput.contains("no modules found"); } /** * Check if a tool is already available on central. * * @param toolId tool id * @param tempWorkspaceDirectory Path to workspace * @param envVariables Environmental variables * @return whether the package is available on central * @throws IOException * @throws InterruptedException */ static boolean isToolAvailableInCentral(String toolId, Path tempWorkspaceDirectory, Map envVariables) throws IOException, InterruptedException { String buildOutput = searchToolDetails(toolId, tempWorkspaceDirectory, envVariables); return !buildOutput.contains("no tools found"); } /** * Returns true if the provided version is available on central. * * @param pkg package * @param tempWorkspaceDirectory Path to workspace * @param envVariables Environmental variables * @param version version to check availability * @return * @throws IOException * @throws InterruptedException */ static boolean isPkgVersionAvailableInCentral(String pkg, Path tempWorkspaceDirectory, Map envVariables, String version) throws IOException, InterruptedException { String buildOutput = searchPackageDetails(pkg, tempWorkspaceDirectory, envVariables); return (!buildOutput.contains("no modules found") && buildOutput.contains(version)); } /** * Search for the given package on Central. * * @param pkg package * @param tempWorkspaceDirectory Path to workspace * @param envVariables Environmental variables * @return * @throws IOException * @throws InterruptedException */ private static String searchPackageDetails(String pkg, Path tempWorkspaceDirectory, Map envVariables) throws IOException, InterruptedException { Process search = executeSearchCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new LinkedList<>(Collections.singletonList(pkg)), envVariables); String buildErrors = getString(search.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } return getString(search.getInputStream()); } /** * Search for the given package on Central. * * @param toolId tool id * @param tempWorkspaceDirectory Path to workspace * @param envVariables Environmental variables * @return * @throws IOException * @throws InterruptedException */ private static String searchToolDetails(String toolId, Path tempWorkspaceDirectory, Map envVariables) throws IOException, InterruptedException { Process search = executeToolCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory, new ArrayList<>(Arrays.asList("search", toolId)), envVariables); String buildErrors = getString(search.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } return getString(search.getInputStream()); } /** * Build bala for a package. * * @param tempWorkspaceDirectory Path to workspace * @param envVariables Environmental variables * @param packageName Package * @param orgName Organization name * @param version Package Version * @param additionalArgs Additional arguments to pass wben building package. * @throws IOException * @throws InterruptedException */ static void buildPackageBala(Path tempWorkspaceDirectory, Map envVariables, String packageName, String orgName, String version, List additionalArgs) throws IOException, InterruptedException { List argsCollection = new ArrayList(additionalArgs); Process build = executePackCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory.resolve(packageName), new LinkedList<>(argsCollection), envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(build.getInputStream()); if (!buildOutput.contains(getGenerateBalaLog(orgName, packageName, ANY_PLATFORM, version))) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + getGenerateBalaLog(orgName, packageName, ANY_PLATFORM, version)); } Assert.assertTrue( getBalaPath(tempWorkspaceDirectory.resolve(packageName), orgName, packageName, ANY_PLATFORM, version).toFile().exists()); } /** * Push a bala to remote repository. * * @param tempWorkspaceDirectory Path to workspace * @param projectName Project name * @param envVariables Environmental variables * @param orgName Organization name * @param packageName package * @param version Package Version * @throws IOException * @throws InterruptedException */ static void testPushPackage(Path tempWorkspaceDirectory, String projectName, Map envVariables, String orgName, String packageName, String version) throws IOException, InterruptedException { Process build = executePushCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory.resolve(projectName), new LinkedList<>(), envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(build.getInputStream()); if (!buildOutput.contains(getPushedToCentralLog(orgName, packageName, version))) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + getPushedToCentralLog(orgName, packageName)); } } /** * Push a bala to `local` repository. * * @param tempWorkspaceDirectory Path to workspace * @param projectName Project name * @param envVariables Environmental variables * @param orgName Organization name * @param packageName package * @param version Package Version * @throws IOException * @throws InterruptedException */ static void testPushPackageToLocal(Path tempWorkspaceDirectory, String projectName, Map envVariables, String orgName, String packageName, String version) throws IOException, InterruptedException { Process build = executePushCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory.resolve(projectName), new LinkedList<>(Collections.singletonList("--repository=local")), envVariables); // The success message is pushed to error stream for this local push. String buildOutput = getString(build.getInputStream()); String expectedLog = "Successfully pushed target/bala/" + orgName + "-" + packageName + "-any-" + version + ".bala to 'local' repository."; if (!buildOutput.contains(expectedLog)) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + expectedLog); } } /** * Push a bala to central using bala path. * * @param tempWorkspaceDirectory Path to workspace * @param projectName Project name * @param envVariables Environmental variables * @param orgName Organization name * @param packageName package * @param version Package Version * @param balaPath Path to the bala file to be pushed * @throws IOException * @throws InterruptedException */ public static void testPushPackageUsingBalaPath(Path tempWorkspaceDirectory, String projectName, Map envVariables, String orgName, String packageName, String version, String balaPath) throws IOException, InterruptedException { Process build = executePushCommand(DISTRIBUTION_FILE_NAME, tempWorkspaceDirectory.resolve(projectName), new LinkedList<>(Collections.singletonList(balaPath)), envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(build.getInputStream()); if (!buildOutput.contains(getPushedToCentralLog(orgName, packageName, version))) { Assert.fail(OUTPUT_NOT_CONTAINS_EXP_MSG + getPushedToCentralLog(orgName, packageName)); } } public static void deleteBalaOfPackage(String orgName, String packageName) { Path balaPath = ProjectUtils.createAndGetHomeReposPath().resolve(ProjectConstants.REPOSITORIES_DIR) .resolve(ProjectConstants.CENTRAL_REPOSITORY_CACHE_NAME).resolve(ProjectConstants.BALA_DIR_NAME) .resolve(orgName); Path packagePath = balaPath.resolve(packageName); ProjectUtils.deleteDirectory(packagePath); } public static List getFileListByExtension(Path filesDirectory, String extension) throws IOException { try (Stream packagesPathWalk = Files.walk(filesDirectory)) { return packagesPathWalk .filter(Files::isRegularFile) .filter(p -> p.toString().endsWith(extension)) .collect(Collectors.toList()); } } public static void replacePackageName(List filesToChange, String packageName, String randomPackageName) throws IOException { for (Path fileToChange : filesToChange) { String oldContent = ""; BufferedReader reader = new BufferedReader(new FileReader(fileToChange.toFile())); String line = reader.readLine(); while (line != null) { oldContent = oldContent + line + System.lineSeparator(); line = reader.readLine(); } if (oldContent.contains(packageName)) { String newContent = oldContent.replaceAll(packageName, randomPackageName); FileWriter writer = new FileWriter(fileToChange.toFile()); writer.write(newContent); writer.close(); } reader.close(); } } public static String getNewDirectoryName(String currentPackageName, String randomSuffix) { String packageNameSplit = Arrays.stream(currentPackageName.split("\\.")). filter((b) -> b.startsWith("Package")).toArray()[0].toString(); return currentPackageName.replace(packageNameSplit, packageNameSplit + PACKAGE_NAME_SEPARATOR + randomSuffix); } public static void replaceRandomPackageName(Path tempWorkspaceDirectory, String randomPackageName, String packageName) throws IOException { List balFiles = CentralTestUtils.getFileListByExtension(tempWorkspaceDirectory, "bal"); List tomlFiles = CentralTestUtils.getFileListByExtension(tempWorkspaceDirectory, "toml"); CentralTestUtils.replacePackageName(balFiles, packageName, randomPackageName); CentralTestUtils.replacePackageName(tomlFiles, packageName, randomPackageName); } } ================================================ FILE: project-api-tests/src/test/java/org/ballerina/projectapi/DistributionCompatibilityTest.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.projectapi; import org.apache.commons.io.FileUtils; import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; import static org.ballerina.projectapi.CentralTestUtils.DEPENDENCIES_TOML; import static org.ballerina.projectapi.CentralTestUtils.PACKAGE_NAME_SEPARATOR; import static org.ballerina.projectapi.CentralTestUtils.buildPackageBala; import static org.ballerina.projectapi.CentralTestUtils.createSettingToml; import static org.ballerina.projectapi.CentralTestUtils.deleteFiles; import static org.ballerina.projectapi.CentralTestUtils.getEnvVariables; import static org.ballerina.projectapi.CentralTestUtils.getExecutableJarPath; import static org.ballerina.projectapi.CentralTestUtils.getString; import static org.ballerina.projectapi.CentralTestUtils.replaceRandomPackageName; import static org.ballerina.projectapi.CentralTestUtils.testPushPackage; import static org.ballerina.projectapi.TestUtils.DISTRIBUTION_FILE_NAME; import static org.ballerina.projectapi.TestUtils.OUTPUT_CONTAIN_ERRORS; import static org.ballerina.projectapi.TestUtils.executeBuildCommand; /** * Tests related to dependency resolution with distribution compatibility restrictions in central. */ public class DistributionCompatibilityTest { private Path tempHomeDirectory; private Path tempWorkspaceDirectory; private Map envVariables; private String orgName = "bctestorg"; private String projectPath = "distribution-tests"; private String randomPackageSuffix; private static final int BUFFER_SIZE = 4096; @BeforeClass() public void setUp() throws IOException, InterruptedException { TestUtils.setupDistributions(); tempHomeDirectory = Files.createTempDirectory("bal-test-integration-packaging-home-"); tempWorkspaceDirectory = Files.createTempDirectory("bal-test-integration-packaging-workspace-"); createSettingToml(tempHomeDirectory); envVariables = TestUtils.addEnvVariables(getEnvVariables(), tempHomeDirectory); // Copy test resources to temp workspace directory try { URI testResourcesURI = Objects.requireNonNull( getClass().getClassLoader().getResource(projectPath)).toURI(); Files.walkFileTree(Paths.get(testResourcesURI), new CentralTest.Copy(Paths.get(testResourcesURI), this.tempWorkspaceDirectory)); } catch (URISyntaxException e) { Assert.fail("error loading resources"); } String dependencyVersion = "1.1.0"; randomPackageSuffix = CentralTestUtils.randomPackageName(6); String multiPackageName = "disttestmultiples"; String updatedMultiPackageName = multiPackageName + PACKAGE_NAME_SEPARATOR + randomPackageSuffix; // Update directory to match the package name replaceContainingDir(multiPackageName, updatedMultiPackageName); modifyBala(multiPackageName, updatedMultiPackageName); replaceRandomPackageName(tempWorkspaceDirectory, updatedMultiPackageName, multiPackageName); // Push dependency built with beta6, 9999.0.0 and a 1.1.0 version of multiple dependencies List dependencyPackages = Arrays.asList("disttestpackbeta6", "forwardpack1", updatedMultiPackageName); for (String dependencyPackageName : dependencyPackages) { if (!CentralTestUtils.isPkgAvailableInCentral(orgName + "/" + dependencyPackageName, tempWorkspaceDirectory, envVariables)) { CentralTestUtils.testPushPackageUsingBalaPath(tempWorkspaceDirectory, dependencyPackageName, envVariables, orgName, dependencyPackageName, dependencyVersion, tempWorkspaceDirectory.resolve(dependencyPackageName).resolve(orgName + "-" + dependencyPackageName + "-any-" + dependencyVersion + ".bala").toString()); } } // Delete the bala file for multiple dependencies FileUtils.forceDelete(FileUtils.getFile(String.valueOf(tempWorkspaceDirectory.resolve(updatedMultiPackageName). resolve(orgName + "-" + updatedMultiPackageName + "-any-" + dependencyVersion + ".bala")))); // Dependency built with this ballerina version String dependencyPackage = "disttestpack1"; String updatedDependencyPackage = dependencyPackage + PACKAGE_NAME_SEPARATOR + randomPackageSuffix; replaceRandomPackageName(tempWorkspaceDirectory, updatedDependencyPackage, dependencyPackage); replaceContainingDir(dependencyPackage, updatedDependencyPackage); if (!CentralTestUtils.isPkgAvailableInCentral(orgName + "/" + updatedDependencyPackage, tempWorkspaceDirectory, envVariables)) { pushToCentral(updatedDependencyPackage, dependencyVersion); } } private void replaceContainingDir(String packageName, String updatedPackageName) { File multiplesDir = new File(tempWorkspaceDirectory.toFile(), packageName); File updatedMultiplesDir = new File(tempWorkspaceDirectory.toFile(), updatedPackageName); if (multiplesDir.exists()) { multiplesDir.renameTo(updatedMultiplesDir); } } @Test(description = "Verify dependency resolution with a dependency built with same distribution version.") public void testDependencyResolutionWithSameDist() throws IOException, InterruptedException { String dependencyPackageName = "disttestpack1" + PACKAGE_NAME_SEPARATOR + randomPackageSuffix; String dependencyVersion = "1.1.0"; String packageName = "disttestpack2"; buildPackage(packageName, new LinkedList<>()); verifyUpdatedDependencies(packageName, "org = \"" + orgName + "\"\n" + "name = \"" + dependencyPackageName + "\"\n" + "version = \"" + dependencyVersion + "\""); } @Test(description = "Verify dependency resolution with a dependency built with an older distribution version.") public void testDependencyResolutionBackwardCompatibility() throws IOException, InterruptedException { String dependencyPackageName = "disttestpackbeta6"; String dependencyVersion = "1.1.0"; String packageName = "disttestpack3"; buildPackage(packageName, new LinkedList<>()); verifyUpdatedDependencies(packageName, "org = \"" + orgName + "\"\n" + "name = \"" + dependencyPackageName + "\"\n" + "version = \"" + dependencyVersion + "\""); } @Test(description = "Verify dependency resolution with a dependency built with a future distribution version.") public void testDependencyResolutionForwardCompatibility() throws IOException, InterruptedException { verifyBuildPackage("disttestpack4", "forwardpack1", new LinkedList<>()); } @Test(description = "Verify dependency resolution with multiple dependencies built with an older and " + "current distribution version.") public void testWithMultipleDistDependencyVersions() throws IOException, InterruptedException { // Push 1.1.1 built with this Ballerina version String updatedVersion = "1.1.1"; String dependencyPackage = "disttestmultiples" + PACKAGE_NAME_SEPARATOR + randomPackageSuffix; if (!CentralTestUtils.isPkgVersionAvailableInCentral(orgName + "/" + dependencyPackage, tempWorkspaceDirectory, envVariables, updatedVersion)) { // Build the bala for package buildPackageBala(tempWorkspaceDirectory, envVariables, dependencyPackage, orgName, updatedVersion, Collections.emptyList()); // Push the package to central testPushPackage(tempWorkspaceDirectory, dependencyPackage, envVariables, orgName, dependencyPackage, updatedVersion); } String packageName = "disttestpack5"; buildPackage(packageName, new LinkedList<>()); verifyUpdatedDependencies(packageName, "org = \"" + orgName + "\"\n" + "name = \"" + dependencyPackage + "\"\n" + "version = \"" + updatedVersion + "\""); } private void buildPackage(String packageName, LinkedList args) throws IOException, InterruptedException { Process build = executeBuildCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve(packageName), args, this.envVariables); try (InputStream errStream = build.getErrorStream()) { String buildErrors = getString(errStream); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } Assert.assertTrue( getExecutableJarPath(this.tempWorkspaceDirectory.resolve(packageName), packageName) .toFile().exists()); } } private void pushToCentral(String packageName, String version) throws IOException, InterruptedException { if (!CentralTestUtils.isPkgAvailableInCentral(orgName + "/" + packageName, tempWorkspaceDirectory, envVariables)) { // Build the bala for package buildPackageBala(tempWorkspaceDirectory, envVariables, packageName, orgName, version, Collections.emptyList()); // Push the package to central testPushPackage(tempWorkspaceDirectory, packageName, envVariables, orgName, packageName, version); } } private void verifyUpdatedDependencies(String packageName, String dependencyUpdate) throws IOException { Path filePath = tempWorkspaceDirectory.resolve(packageName).resolve(DEPENDENCIES_TOML); String content = Files.readString(filePath); Assert.assertTrue(content.contains(dependencyUpdate)); } private void verifyBuildPackage(String packageName, String dependencyPackage, LinkedList args) throws IOException, InterruptedException { Process build = executeBuildCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve(packageName), args, this.envVariables); try (InputStream errStream = build.getErrorStream()) { String buildErrors = getString(errStream); if (!buildErrors.isEmpty()) { Assert.assertTrue(buildErrors.contains("cannot resolve module '" + orgName + "/" + dependencyPackage + " as _'")); } else { Assert.fail("Build output does not contain the expected error"); } } } private void modifyBala(String packageName, String updatedPackageName) throws IOException { // Open the zip file String dependencyVersion = "1.1.0"; Path sourcePath = this.tempWorkspaceDirectory.resolve(updatedPackageName).resolve(orgName + "-" + packageName + "-any-" + dependencyVersion + ".bala"); File zipFile = new File(sourcePath.toString()); ZipFile zip = new ZipFile(zipFile); // Create a new zip file with the updated contents Path packageDir = this.tempWorkspaceDirectory.resolve(updatedPackageName); if (!packageDir.toFile().exists()) { Files.createDirectories(packageDir); } Path destinationPath = this.tempWorkspaceDirectory.resolve(updatedPackageName).resolve(orgName + "-" + updatedPackageName + "-any-" + dependencyVersion + ".bala"); // Create a temporary directory for the new contents File tempDir = Files.createTempDirectory("zipFileModification").toFile(); // Extract the zip file to the temporary directory zip.stream() .forEach(entry -> { try { String entryName = entry.getName(); File entryFile = new File(tempDir, entryName); if (entry.isDirectory()) { entryFile.mkdirs(); } else { entryFile.getParentFile().mkdirs(); FileOutputStream outputStream = new FileOutputStream(entryFile); InputStream inputStream = zip.getInputStream(entry); byte[] buffer = new byte[BUFFER_SIZE]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) > 0) { outputStream.write(buffer, 0, bytesRead); } outputStream.close(); inputStream.close(); } } catch (IOException e) { throw new UncheckedIOException(e); } }); // Rename the disttestmultiples module directory to disttestmultiples_ File disttestmultiplesDir = new File(tempDir, "modules/" + packageName); if (disttestmultiplesDir.exists()) { File disttestmultiplesXyztheDir = new File(tempDir, "modules/" + updatedPackageName); disttestmultiplesDir.renameTo(disttestmultiplesXyztheDir); } Path packageJsonFile = Paths.get(tempDir.toString(), "package.json"); if (packageJsonFile.toFile().exists()) { String content = new String(Files.readAllBytes(packageJsonFile)); content = content.replace(packageName, updatedPackageName); Files.write(packageJsonFile, content.getBytes()); } Path dependenciesJsonFile = Paths.get(tempDir.toString(), "dependency-graph.json"); if (dependenciesJsonFile.toFile().exists()) { String content = new String(Files.readAllBytes(dependenciesJsonFile)); content = content.replace(packageName, updatedPackageName); Files.write(dependenciesJsonFile, content.getBytes()); } createBala(List.of(tempDir.listFiles()), destinationPath.toString()); // Clean up zip.close(); FileUtils.deleteDirectory(tempDir); } private void createBala(List listFiles, String destZipFile) throws FileNotFoundException, IOException { ZipOutputStream out = new ZipOutputStream(new FileOutputStream(destZipFile)); for (File file : listFiles) { if (file.isDirectory()) { zipDirectory(file, file.getName(), out); } else { zipFile(file, out); } } out.flush(); out.close(); } private void zipFile(File file, ZipOutputStream out) throws IOException { out.putNextEntry(new ZipEntry(file.getName())); BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream( file)); long bytesRead = 0; byte[] bytesIn = new byte[BUFFER_SIZE]; int read = 0; while ((read = inputStream.read(bytesIn)) != -1) { out.write(bytesIn, 0, read); bytesRead += read; } out.closeEntry(); } private void zipDirectory(File folder, String parentFolder, ZipOutputStream out) throws IOException { for (File file : folder.listFiles()) { if (file.isDirectory()) { zipDirectory(file, parentFolder + "/" + file.getName(), out); continue; } out.putNextEntry(new ZipEntry(parentFolder + "/" + file.getName())); BufferedInputStream bis = new BufferedInputStream( new FileInputStream(file)); long bytesRead = 0; byte[] bytes = new byte[BUFFER_SIZE]; int read = 0; while ((read = bis.read(bytes)) != -1) { out.write(bytes, 0, read); bytesRead += read; } out.closeEntry(); } } @AfterClass private void cleanup() throws IOException { deleteFiles(tempHomeDirectory); deleteFiles(tempWorkspaceDirectory); } } ================================================ FILE: project-api-tests/src/test/java/org/ballerina/projectapi/HierarchicalPackagesTest.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.projectapi; import org.apache.commons.io.FileUtils; 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.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import static java.nio.file.StandardOpenOption.APPEND; import static org.ballerina.projectapi.CentralTestUtils.BALLERINA_ARTIFACT_TYPE; import static org.ballerina.projectapi.CentralTestUtils.BALLERINA_HOME_DIR; import static org.ballerina.projectapi.CentralTestUtils.BALLERINA_TOML; import static org.ballerina.projectapi.CentralTestUtils.COMMON_VERSION; import static org.ballerina.projectapi.CentralTestUtils.DEPENDENCIES_TOML; import static org.ballerina.projectapi.CentralTestUtils.PACKAGE_NAME_SEPARATOR; import static org.ballerina.projectapi.CentralTestUtils.PACKAGE_PATH_SEPARATOR; import static org.ballerina.projectapi.CentralTestUtils.buildPackageBala; import static org.ballerina.projectapi.CentralTestUtils.createSettingToml; import static org.ballerina.projectapi.CentralTestUtils.deleteFiles; import static org.ballerina.projectapi.CentralTestUtils.getEnvVariables; import static org.ballerina.projectapi.CentralTestUtils.getExecutableJarPath; import static org.ballerina.projectapi.CentralTestUtils.getNewDirectoryName; import static org.ballerina.projectapi.CentralTestUtils.getString; import static org.ballerina.projectapi.CentralTestUtils.replaceRandomPackageName; import static org.ballerina.projectapi.CentralTestUtils.testPushPackage; import static org.ballerina.projectapi.CentralTestUtils.testPushPackageToLocal; import static org.ballerina.projectapi.TestUtils.DISTRIBUTION_FILE_NAME; import static org.ballerina.projectapi.TestUtils.OUTPUT_CONTAIN_ERRORS; import static org.ballerina.projectapi.TestUtils.executeBuildCommand; /** * Tests related to hierarchical packages. */ public class HierarchicalPackagesTest { private Path tempHomeDirectory; private Path tempWorkspaceDirectory; private Map envVariables; private String randomPackageSuffix; private String orgName = "bctestorg"; private String projectPath = "hierarchical-packages"; private String updatedVersion = "1.0.1"; private final String balExtension = ".bal"; private final String modulesPath = "modules"; @BeforeClass() public void setUp() throws IOException, InterruptedException { TestUtils.setupDistributions(); tempHomeDirectory = Files.createTempDirectory("bal-test-integration-packaging-home-"); tempWorkspaceDirectory = Files.createTempDirectory("bal-test-integration-packaging-workspace-"); createSettingToml(tempHomeDirectory); envVariables = TestUtils.addEnvVariables(getEnvVariables(), tempHomeDirectory); // Copy test resources to temp workspace directory try { URI testResourcesURI = Objects.requireNonNull( getClass().getClassLoader().getResource(projectPath)).toURI(); Files.walkFileTree(Paths.get(testResourcesURI), new CentralTest.Copy(Paths.get(testResourcesURI), this.tempWorkspaceDirectory)); } catch (URISyntaxException e) { Assert.fail("error loading resources"); } // Update the distribution-version in Dependencies.toml Files.list(tempWorkspaceDirectory).forEach(path -> { File dependenciesTomlTemplate = path.resolve("Dependencies-template.toml").toFile(); if (dependenciesTomlTemplate.exists()) { try { replaceDependenciesTomlVersion(path); } catch (IOException e) { Assert.fail("error updating Dependencies.toml for " + path.getFileName() + " with error: " + e.getMessage()); } } }); randomPackageSuffix = CentralTestUtils.randomPackageName(6); Set uniquePackageNames = new HashSet<>(); for (String packageName : tempWorkspaceDirectory.toFile().list()) { File newPackageDirName = new File(tempWorkspaceDirectory.resolve( getNewDirectoryName(packageName, randomPackageSuffix)).toUri()); tempWorkspaceDirectory.resolve(packageName).toFile().renameTo(newPackageDirName); uniquePackageNames.add(Arrays.stream(packageName.split("\\.")). filter((b) -> b.startsWith("Package")).toArray()[0].toString()); } for (String packageName : uniquePackageNames) { String randomPackageName = packageName + PACKAGE_NAME_SEPARATOR + randomPackageSuffix; replaceRandomPackageName(tempWorkspaceDirectory, randomPackageName, packageName); } List oldPackageNames = Arrays.asList("PackageH.test", "PackageJ.test", "PackageL.test", "PackageT.test", "PackageH.test.mod", "PackageR.test"); List packageNames = oldPackageNames.stream() .map(pkg -> getNewDirectoryName(pkg, randomPackageSuffix)) .collect(Collectors.toList()); List versions = Arrays.asList(COMMON_VERSION, COMMON_VERSION, COMMON_VERSION, "1.0.0-beta.1", updatedVersion, COMMON_VERSION); for (int i = 0; i < packageNames.size(); i++) { String packageName = packageNames.get(i); String version = versions.get(i); pushToCentral(packageName, version); } // Build and push updated Versions to Central oldPackageNames = Arrays.asList("PackageJ.test", "PackageT.test"); packageNames = oldPackageNames.stream() .map(pkg -> getNewDirectoryName(pkg, randomPackageSuffix)) .collect(Collectors.toList()); List previousVersions = Arrays.asList(COMMON_VERSION, "1.0.0-beta.1"); for (int i = 0; i < packageNames.size(); i++) { String packageName = packageNames.get(i); pushUpdatedVersion(previousVersions, i, packageName); } // Push package to local String localPackageName = getNewDirectoryName("PackageQ.test", randomPackageSuffix); if (!CentralTestUtils.isPkgAvailableInCentral(orgName + PACKAGE_PATH_SEPARATOR + localPackageName, tempWorkspaceDirectory, envVariables)) { // Build the bala for package buildPackageBala(tempWorkspaceDirectory, envVariables, localPackageName, orgName, COMMON_VERSION, Collections.emptyList()); testPushPackageToLocal(tempWorkspaceDirectory, localPackageName, envVariables, orgName, localPackageName, COMMON_VERSION); } } private void pushToCentral(String packageName, String version) throws IOException, InterruptedException { if (!CentralTestUtils.isPkgAvailableInCentral(orgName + PACKAGE_PATH_SEPARATOR + packageName, tempWorkspaceDirectory, envVariables)) { // Build the bala for package buildPackageBala(tempWorkspaceDirectory, envVariables, packageName, orgName, version, Collections.emptyList()); // Push the package to central testPushPackage(tempWorkspaceDirectory, packageName, envVariables, orgName, packageName, version); } } private void pushUpdatedVersion(List previousVersions, int i, String packageName) throws IOException, InterruptedException { if (!CentralTestUtils.isPkgVersionAvailableInCentral(orgName + PACKAGE_PATH_SEPARATOR + packageName, tempWorkspaceDirectory, envVariables, updatedVersion)) { // Update version details in Ballerina.toml updateBallerinaToml(packageName, previousVersions.get(i), updatedVersion, "", ""); // Build the bala for package buildPackageBala(tempWorkspaceDirectory, envVariables, packageName, orgName, updatedVersion, Collections.emptyList()); // Push the package to central testPushPackage(tempWorkspaceDirectory, packageName, envVariables, orgName, packageName, updatedVersion); } } @Test(description = "Verify build package behaviour for hierarchical package imports in two consecutive builds.", enabled = false) public void testConsecutiveBuilds() throws IOException, InterruptedException { String packageName = getNewDirectoryName("PackageI.test", randomPackageSuffix); // First build buildPackage(packageName, new LinkedList<>()); // Consecutive build with existing `Dependencies.toml` and `build` file buildPackage(packageName, new LinkedList<>()); } @Test(description = "Verify build package behaviour when there is an updated version for a hierarchical package" + " import in Remote Repo.") public void testUpdatedVersionInRemote() throws IOException, InterruptedException { String importedPackageName = getNewDirectoryName("PackageJ.test", randomPackageSuffix); String packageName = getNewDirectoryName("PackageK", randomPackageSuffix); // Check whether version in Dependencies.toml is the initial version verifyUpdatedDependencies(packageName, getDependencyUpdate(orgName, importedPackageName, COMMON_VERSION)); // Build package when there is an updated version in Remote Repo buildPackage(packageName, new LinkedList<>()); // Check if version has been updated in Dependencies.toml verifyUpdatedDependencies(packageName, getDependencyUpdate(orgName, importedPackageName, updatedVersion)); } @Test(description = "When a new module is added and published to remote repo and project is updated to use " + "the new module, check if it pulls the latest even when sticky true") public void testUpdatedPackage() throws IOException, InterruptedException { // Check if specific package version is available in central. If not build and push updated version. String importedPackageName = getNewDirectoryName("PackageL.test", randomPackageSuffix); if (!CentralTestUtils.isPkgVersionAvailableInCentral(orgName + PACKAGE_PATH_SEPARATOR + importedPackageName, tempWorkspaceDirectory, envVariables, updatedVersion)) { // Add a module to the package addNewModule(importedPackageName, "doc.api", "mod.api"); // Update version details in Ballerina.toml updateBallerinaToml(importedPackageName, COMMON_VERSION, updatedVersion, "", ""); addExports(importedPackageName, new String[]{importedPackageName + ".doc.api"}); // Build the bala for updated package buildPackageBala(tempWorkspaceDirectory, envVariables, importedPackageName, orgName, updatedVersion, Collections.emptyList()); // Push the package to central testPushPackage(tempWorkspaceDirectory, importedPackageName, envVariables, orgName, importedPackageName, updatedVersion); } String packageName = getNewDirectoryName("PackageM", randomPackageSuffix); // Check whether version in Dependencies.toml is the initial version verifyUpdatedDependencies(packageName, getDependencyUpdate(orgName, importedPackageName, COMMON_VERSION)); // Import newly added module updateImports(packageName, "import " + orgName + "/" + importedPackageName + ".doc.api as _;", "main.bal"); // Build updated package with sticky buildPackage(packageName, new LinkedList<>(Collections.singletonList("--sticky"))); // Check if version has been updated in Dependencies.toml verifyUpdatedDependencies(packageName, getDependencyUpdate(orgName, importedPackageName, updatedVersion)); } @Test(description = "Verify whether the correct packages are picked when there are two possible packages" + " for an import") public void testTwoPossiblePackages() throws IOException, InterruptedException { // Check if specific package version is available in central. If not build and push updated version. String importedPackageName = getNewDirectoryName("PackageH.test.mod", randomPackageSuffix); // PackageN has already locked `orgName/PackageH.test:1.0.0` String packageName = getNewDirectoryName("PackageN", randomPackageSuffix); // Import newly added module in new package `orgName/PackageH.test.mod:1.0.1` updateImports(packageName, "import " + orgName + "/" + importedPackageName + ".api.doc as _;", "main.bal"); // Build updated package with sticky buildPackage(packageName, new LinkedList<>(Collections.singletonList("--sticky"))); // Check if version has been updated in Dependencies.toml verifyUpdatedDependencies(packageName, getDependencyUpdate(orgName, importedPackageName, updatedVersion)); // Build package when there is an updated version in Remote Repo buildPackage(packageName, new LinkedList<>()); } @Test(description = "Verify build package behaviour if hierarchical package needs to be pulled from local " + "repository.") public void testPackageFromLocal() throws IOException, InterruptedException { String packageName = getNewDirectoryName("PackageR", randomPackageSuffix); // Create symbolic link for this package in local repository Path target = Paths.get(envVariables.get(BALLERINA_HOME_DIR)).resolve("repositories"). resolve("local").resolve(BALLERINA_ARTIFACT_TYPE).resolve(orgName); Path link = Paths.get(envVariables.get("HOME")).resolve(".ballerina").resolve("repositories"). resolve("local").resolve(BALLERINA_ARTIFACT_TYPE).resolve(orgName); File linkDir = new File(link.toUri()); if (linkDir.exists()) { FileUtils.deleteDirectory(linkDir); } Files.createDirectories(link.getParent()); Files.createSymbolicLink(link, target); buildPackage(packageName, new LinkedList<>()); // Delete symbolic link for this package in local repository if (linkDir.exists()) { FileUtils.deleteDirectory(linkDir); } } @Test(description = "Verify build package behaviour when there is an updated version for a transitive " + "hierarchical package dependency in Remote Repo.") public void testUpdateTransitiveDependency() throws IOException, InterruptedException { String transitivePackagePrefix = getNewDirectoryName("Transitive.PackageH.test", randomPackageSuffix); String randomSuffix = CentralTestUtils.randomPackageName(5); String transitivePackageName = transitivePackagePrefix + PACKAGE_NAME_SEPARATOR + randomSuffix; String importedPackagePrefix = getNewDirectoryName("PackageO.test", randomPackageSuffix); String importedPackageName = importedPackagePrefix + PACKAGE_NAME_SEPARATOR + randomSuffix; String packageName = getNewDirectoryName("PackageP", randomPackageSuffix); pushPreRequisites(transitivePackagePrefix, transitivePackageName, importedPackagePrefix, importedPackageName, packageName); // Build the package that uses `importedPackageName` package buildPackage(packageName, new LinkedList<>()); // Update transitive package version and push pushUpdatedVersion(Arrays.asList(COMMON_VERSION), 0, transitivePackageName); // Build the package that uses `importedPackageName` package with sticky true buildPackage(packageName, new LinkedList<>(Collections.singletonList("--sticky"))); // Check the old package version in Dependencies.toml verifyUpdatedDependencies(packageName, getDependencyUpdate(orgName, transitivePackageName, COMMON_VERSION)); // Rebuild after clearing target Path targetDir = tempWorkspaceDirectory.resolve(packageName).resolve("target"); FileUtils.deleteDirectory(targetDir.toFile()); buildPackage(packageName, new LinkedList<>()); // Check if version has been updated in Dependencies.toml verifyUpdatedDependencies(packageName, getDependencyUpdate(orgName, transitivePackageName, updatedVersion)); } private void pushPreRequisites(String transitivePackagePrefix, String transitivePackageName, String importedPackagePrefix, String importedPackageName, String packageName) throws IOException, InterruptedException { // Copy transitivePackage template as a new project copyProject(transitivePackagePrefix, transitivePackageName); // Copy importedPackage template as a new project copyProject(importedPackagePrefix, importedPackageName); // Build and push transitivePackage pushToCentral(transitivePackageName, COMMON_VERSION); // Build and push imported package updateImports(importedPackageName, "import " + orgName + "/" + transitivePackageName + " as _;", "lib.bal"); pushToCentral(importedPackageName, COMMON_VERSION); updateImports(packageName, "import " + orgName + "/" + importedPackageName + " as _;", "main.bal"); } private void copyProject(String packagePrefix, String packageName) throws IOException { Files.createDirectories(tempWorkspaceDirectory.resolve(packageName)); Files.walkFileTree(tempWorkspaceDirectory.resolve(packagePrefix), new CentralTest.Copy(tempWorkspaceDirectory.resolve(packagePrefix), tempWorkspaceDirectory.resolve(packageName))); updateBallerinaToml(packageName, COMMON_VERSION, COMMON_VERSION, "\"" + packagePrefix + "\"", "\"" + packageName + "\""); } @Test(description = "Verify build package behaviour when there is direct dependency added for another package " + "with similar hierarchical name structure as a transitive dependency.") public void testTwoPossibleTransitiveDependencies() throws IOException, InterruptedException { String transitivePackageName = getNewDirectoryName("PackageH.test", randomPackageSuffix); String directPackageName = getNewDirectoryName("PackageH.test.mod", randomPackageSuffix); String packageName = getNewDirectoryName("PackageS", randomPackageSuffix); // Build package with transitive dependency to "PackageH.test" // PackageS <— PackageR.test <— PackageH.test buildPackage(packageName, new LinkedList<>(Collections.emptyList())); verifyUpdatedDependencies(packageName, getDependencyUpdate(orgName, transitivePackageName, COMMON_VERSION)); // Add direct dependency for "PackageH.test.mod" and build updateImports(packageName, "import " + orgName + "/" + directPackageName + ".api.doc as _;", "main.bal"); buildPackage(packageName, new LinkedList<>(Collections.emptyList())); verifyUpdatedDependencies(packageName, getDependencyUpdate(orgName, directPackageName, updatedVersion)); } // TODO: This test is disabled due to a bug in Central API. Need to re-enable once issue is fixed. @Test(description = "Verify build package behaviour when there is an updated version for a pre-release version " + "of a hierarchical package import in Remote Repo.", enabled = false) public void testUpdateToPreReleaseVersionInRemote() throws IOException, InterruptedException { String importedPackageName = getNewDirectoryName("PackageT.test", randomPackageSuffix); String packageName = getNewDirectoryName("PackageU", randomPackageSuffix); buildPackage(packageName, new LinkedList<>()); // Check if version has been updated in Dependencies.toml verifyUpdatedDependencies(packageName, getDependencyUpdate(orgName, importedPackageName, updatedVersion)); } private void buildPackage(String packageName, LinkedList args) throws IOException, InterruptedException { Process build = executeBuildCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve(packageName), args, this.envVariables); try (InputStream errStream = build.getErrorStream()) { String buildErrors = getString(errStream); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } Assert.assertTrue( getExecutableJarPath(this.tempWorkspaceDirectory.resolve(packageName), packageName) .toFile().exists()); } } private void verifyUpdatedDependencies(String packageName, String dependencyUpdate) throws IOException { Path filePath = tempWorkspaceDirectory.resolve(packageName).resolve(DEPENDENCIES_TOML); String content = Files.readString(filePath); Assert.assertTrue(content.contains(dependencyUpdate)); } private void addNewModule(String packageName, String newModule, String moduleToCopy) throws IOException { Path filePath = tempWorkspaceDirectory.resolve(packageName).resolve(modulesPath).resolve(newModule); Files.createDirectory(filePath); Files.createFile(filePath.resolve(newModule + balExtension)); Path filePathToCopy = tempWorkspaceDirectory.resolve(packageName).resolve(modulesPath).resolve(moduleToCopy). resolve(moduleToCopy + balExtension); Stream lines = Files.lines(filePathToCopy); Files.write(filePath.resolve(newModule + balExtension), lines.collect(Collectors.toList())); lines.close(); } private void updateBallerinaToml(String packageName, String previousVersion, String updatedVersion, String previousExport, String newExport) throws IOException { Path filePath = tempWorkspaceDirectory.resolve(packageName).resolve(BALLERINA_TOML); Stream lines = Files.lines(filePath); List replaced = lines.map(line -> { String updatedLine = line.replaceAll(previousVersion, updatedVersion); if (!(previousExport.isEmpty() && newExport.isEmpty())) { updatedLine = updatedLine.replaceAll(previousExport, newExport); } return updatedLine; }).collect(Collectors.toList()); Files.write(filePath, replaced); lines.close(); } private void addExports(String packageName, String[] moduleNames) throws IOException { Path filePath = tempWorkspaceDirectory.resolve(packageName).resolve(BALLERINA_TOML); List exports = new ArrayList<>(); for (String mod : moduleNames) { exports.add("\n"); exports.add("[[package.modules]]"); exports.add("name = \"" + mod + "\""); exports.add("export = true"); } Files.write(filePath, exports, APPEND); } private void updateImports(String packageName, String s, String fileName) throws IOException { Path filePath = tempWorkspaceDirectory.resolve(packageName).resolve(fileName); Stream lines = Files.lines(filePath); List updated = new ArrayList<>(); updated.add(s); updated.addAll(lines.collect(Collectors.toList())); Files.write(filePath, updated); lines.close(); } private String getDependencyUpdate(String orgName, String packageName, String updatedVersion) { return "org = \"" + orgName + "\"\n" + "name = \"" + packageName + "\"\n" + "version = \"" + updatedVersion + "\""; } private void replaceDependenciesTomlVersion(Path projectPath) throws IOException { String currentDistrVersion = System.getProperty("short.version"); Path dependenciesTomlTemplatePath = projectPath.resolve("Dependencies-template.toml"); Path dependenciesTomlPath = projectPath.resolve("Dependencies.toml"); try (FileInputStream input = new FileInputStream(dependenciesTomlTemplatePath.toString()); FileOutputStream output = new FileOutputStream(dependenciesTomlPath.toString()); BufferedReader reader = new BufferedReader(new InputStreamReader(input)); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output))) { String line; while ((line = reader.readLine()) != null) { line = line.replace("**INSERT_DISTRIBUTION_VERSION_HERE**", currentDistrVersion); writer.write(line); writer.newLine(); } } } @AfterClass private void cleanup() throws IOException { deleteFiles(tempHomeDirectory); deleteFiles(tempWorkspaceDirectory); } } ================================================ FILE: project-api-tests/src/test/java/org/ballerina/projectapi/MavenCustomRepoTest.java ================================================ /* * Copyright (c) 2023, WSO2 LLC. (http://wso2.com). * * 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.ballerina.projectapi; import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeGroups; import org.testng.annotations.Test; import java.io.File; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.StandardCopyOption; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import static org.ballerina.projectapi.MavenCustomRepoTestUtils.createSettingToml; import static org.ballerina.projectapi.MavenCustomRepoTestUtils.deleteArtifacts; import static org.ballerina.projectapi.MavenCustomRepoTestUtils.deleteFiles; import static org.ballerina.projectapi.MavenCustomRepoTestUtils.editVersionBallerinaToml; import static org.ballerina.projectapi.MavenCustomRepoTestUtils.getEnvVariables; import static org.ballerina.projectapi.MavenCustomRepoTestUtils.getString; import static org.ballerina.projectapi.MavenCustomRepoTestUtils.packTrigger; import static org.ballerina.projectapi.MavenCustomRepoTestUtils.pushTrigger; import static org.ballerina.projectapi.MavenCustomRepoTestUtils.pasteStaticMainBalWithAllPkgs; import static org.ballerina.projectapi.MavenCustomRepoTestUtils.pasteStaticMainBalWithPkg1AndPkg2; import static org.ballerina.projectapi.MavenCustomRepoTestUtils.updateVersionForPackage; import static org.ballerina.projectapi.TestUtils.DISTRIBUTION_FILE_NAME; import static org.ballerina.projectapi.TestUtils.OUTPUT_CONTAIN_ERRORS; import static org.ballerina.projectapi.TestUtils.executeBuildCommand; import static org.ballerina.projectapi.TestUtils.executeCleanCommand; import static org.ballerina.projectapi.TestUtils.executePackCommand; import static org.ballerina.projectapi.TestUtils.executePullCommand; import static org.ballerina.projectapi.TestUtils.executePushCommand; public class MavenCustomRepoTest { private static final String org = "bctestorg"; private static final String platform = "any"; private static final String PACKAGE_NAME = "pkg1"; static final String GITHUB_REPO_ID = "github1"; private static final String VERSION = "0.1.0"; private Path actualHomeDirectory; private Path tempWorkspaceDirectory; private Path actualHomeDirectoryClone; private Map envVariables; @BeforeClass() public void setUp() throws IOException, InterruptedException { TestUtils.setupDistributions(); deleteArtifacts(org, "pkg1"); deleteArtifacts(org, "pkg2"); deleteArtifacts(org, "pkg3"); actualHomeDirectory = Paths.get(System.getProperty("user.home")).resolve(".ballerina"); actualHomeDirectoryClone = Files.createTempDirectory("bal-test-integration-packaging-home-") .resolve(".ballerina"); Files.walkFileTree(actualHomeDirectory, new MavenCustomRepoTest.Copy(actualHomeDirectory, actualHomeDirectoryClone)); deleteFiles(actualHomeDirectory, true); tempWorkspaceDirectory = Files.createTempDirectory("bal-test-integration-packaging-workspace-"); createSettingToml(actualHomeDirectory); System.setProperty("user.home", actualHomeDirectory.getParent().toString()); envVariables = TestUtils.addEnvVariables(getEnvVariables(), actualHomeDirectory); // Copy test resources to temp workspace directory try { URI testResourcesURI = Objects.requireNonNull(getClass().getClassLoader() .getResource("maven-repos")).toURI(); Files.walkFileTree(Paths.get(testResourcesURI), new MavenCustomRepoTest.Copy(Paths.get(testResourcesURI), this.tempWorkspaceDirectory)); } catch (URISyntaxException e) { Assert.fail("error loading resources"); } publishBalaPackagesBeforeTests(); } @Test(description = "Push package to Github packages", enabled = false) public void testPushBalaGithub() throws IOException, InterruptedException { List args = new ArrayList<>(); Process build = executePackCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve(PACKAGE_NAME), args, this.envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(build.getInputStream()); String normalizedOutput = buildOutput.replace("\\", "/").replace("\r\n", "\n").replace("\r", "\n"); String collapsed = normalizedOutput.replaceAll("\\s+", " "); String expectedBalaPath = "target/bala/" + org + "-" + PACKAGE_NAME + "-" + platform + "-" + VERSION + ".bala"; Assert.assertTrue(collapsed.contains("Creating bala") && collapsed.contains(expectedBalaPath), "Expected creation message with path: " + expectedBalaPath + System.lineSeparator() + "Actual output: " + buildOutput); args = new ArrayList<>(); args.add("--repository=" + GITHUB_REPO_ID); build = executePushCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve(PACKAGE_NAME), args, this.envVariables); buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } buildOutput = getString(build.getInputStream()); String normalizedPushOutput = buildOutput.replace("\\", "/").replace("\r\n", "\n").replace("\r", "\n"); String collapsedPush = normalizedPushOutput.replaceAll("\\s+", " "); String expectedPushMsg = "Successfully pushed target/bala/" + org + "-" + PACKAGE_NAME + "-any-" + VERSION + ".bala to '" + GITHUB_REPO_ID + "' repository."; Assert.assertTrue(collapsedPush.contains(expectedPushMsg), "Expected push success message. Actual output: " + buildOutput); } @Test(description = "Pull package from Github packages", dependsOnMethods = "testPushBalaGithub", enabled = false) public void testPullBalaGithub() throws IOException, InterruptedException { List args = new ArrayList<>(); args.add(org + "/" + PACKAGE_NAME + ":" + VERSION); args.add("--repository=" + GITHUB_REPO_ID); Process build = executePullCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve(PACKAGE_NAME), args, this.envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(build.getInputStream()); Assert.assertTrue(buildOutput.contains("Successfully pulled the package from the custom repository")); Path packagePath = this.actualHomeDirectory.resolve("repositories") .resolve(GITHUB_REPO_ID).resolve("bala").resolve(org).resolve(PACKAGE_NAME).resolve(VERSION); Assert.assertTrue(Files.exists(packagePath.resolve("any"))); deleteFiles(this.actualHomeDirectory.resolve("repositories").resolve(GITHUB_REPO_ID), false); } @Test(description = "Build a package offline using a module from Github packages", dependsOnMethods = "testPullBalaGithub", enabled = false) public void testBuildBalaGithubOffline() throws IOException, InterruptedException { List args = new ArrayList<>(); args.add("--offline=true"); Process build = executeBuildCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory .resolve("test-resolution"), args, this.envVariables); String buildErrors = getString(build.getErrorStream()); Assert.assertTrue(buildErrors.contains("cannot resolve module '" + org + "/pact as _'")); } @Test(description = "Build a package Online using a module from Github packages", dependsOnMethods = "testBuildBalaGithubOffline", enabled = false) public void testBuildBalaGithubOnline() throws IOException, InterruptedException { List args = new ArrayList<>(); args.add("--offline=false"); Process build = executeBuildCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory .resolve("test-resolution"), args, this.envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(build.getInputStream()); String normalized = buildOutput.replace("\\", "/").replace("\r\n", "\n").replace("\r", "\n"); String collapsed = normalized.replaceAll("\\s+", " "); Assert.assertTrue(collapsed.contains("Generating executable") && collapsed.contains("target/bin/test.jar"), "Expected generating executable + target/bin/test.jar in output. Actual output: " + buildOutput); } private void publishBalaPackagesBeforeTests() throws IOException, InterruptedException { // Iteratively publish the requested versions: pkg1 -> 0.1.0, pkg2 -> 1.0.0 then pkg 3 -> 1.0.0 String[][] seq = { {"pkg1", "0.1.0"}, {"pkg2", "1.0.0"}, {"pkg3", "1.0.0"} }; for (String[] entry : seq) { String pkg = entry[0]; String ver = entry[1]; // packTrigger returns a Process; we don't use the process object, so don't assign it packTrigger(pkg, this.tempWorkspaceDirectory, this.envVariables); Process push = pushTrigger(pkg, this.tempWorkspaceDirectory, this.envVariables); String pushOutput = getString(push.getInputStream()); String normalizedPushOutput = pushOutput.replace("\\", "/").replace("\r\n", "\n").replace("\r", "\n"); String collapsedPush = normalizedPushOutput.replaceAll("\\s+", " "); Assert.assertTrue(collapsedPush.contains("Successfully pushed target/bala/" + org + "-" + pkg + "-any-" + ver + ".bala to '" + GITHUB_REPO_ID + "' repository."), "Expected push success " + "message. Actual output: " + pushOutput); } } @Test(description = "Build a package with multiple dependencies from Github packages") public void testCase1_buildMyProject_assertDependenciesToml() throws IOException, InterruptedException { List args = new ArrayList<>(); Process build = executeBuildCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory .resolve("myproject1"), args, this.envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(build.getInputStream()); String normalized = buildOutput.replace("\\", "/").replace("\r\n", "\n") .replace("\r", "\n"); String collapsed = normalized.replaceAll("\\s+", " "); Assert.assertTrue(collapsed.contains("Generating executable") && collapsed.contains("target/bin/myproject1.jar"), "Expected generating executable + target/bin/myproject1.jar in output. Actual output: " + buildOutput); Path dependencyPath = this.tempWorkspaceDirectory.resolve("myproject1").resolve("Dependencies.toml"); Optional pkg1Version = MavenCustomRepoTestUtils.getPackageVersionFromDependencies(dependencyPath, "pkg1"); Optional pkg2Version = MavenCustomRepoTestUtils.getPackageVersionFromDependencies(dependencyPath, "pkg2"); Assert.assertTrue(pkg1Version.isPresent(), "Expected pkg1 to be present in Dependencies.toml"); Assert.assertEquals(pkg1Version.get(), "0.1.0", "Package version is not matching with the " + "pushed package version"); Assert.assertTrue(pkg2Version.isPresent(), "Expected pkg2 to be present in Dependencies.toml"); Assert.assertEquals(pkg2Version.get(), "1.0.0", "Package version is not matching with the" + "pushed package version"); } @BeforeGroups("testCase2") public void beforeGroupTestCase2() throws IOException { Path projectDir = this.tempWorkspaceDirectory.resolve("myproject1"); try { Assert.assertTrue(updateVersionForPackage(projectDir, "pkg1", "0.1.0"), "pkg1 not found in Ballerina.toml"); Assert.assertTrue(updateVersionForPackage(projectDir, "pkg2", "1.0.0"), "pkg2 not found in Ballerina.toml"); } catch (IOException e) { Assert.fail("Error updating package versions in Ballerina.toml before Test Case 2. " + e.getMessage()); } pasteStaticMainBalWithPkg1AndPkg2(projectDir); } @Test(description = "Push more packages to Github to test locking mode results", dependsOnMethods = "testCase1_buildMyProject_assertDependenciesToml") public void testCase2_publishAdditionalVersionsForDeps() throws IOException, InterruptedException { // Publish multiple versions for pkg1 (Dep1) String[] pkg1Versions = {"0.1.1", "0.2.0", "1.0.0"}; for (String ver : pkg1Versions) { // Update package version, pack and push editVersionBallerinaToml(this.tempWorkspaceDirectory.resolve("pkg1"), ver); // packTrigger returns a Process we don't need; call directly packTrigger("pkg1", this.tempWorkspaceDirectory, this.envVariables); Process p = pushTrigger("pkg1", this.tempWorkspaceDirectory, this.envVariables); String pushOutput = getString(p.getInputStream()); // Normalize output so Windows backslashes don't break the assertion String normalizedPushOutput = pushOutput.replace("\\", "/") .replace("\r\n", "\n").replace("\r", "\n"); String collapsedPush = normalizedPushOutput.replaceAll("\\s+", " "); String expectedPushMsg1 = "Successfully pushed target/bala/" + org + "-pkg1-any-" + ver + ".bala to '" + GITHUB_REPO_ID + "' repository."; Assert.assertTrue(collapsedPush.contains(expectedPushMsg1), "Expected push success message. Actual output: " + pushOutput); } // Publish multiple versions for pkg2 (Dep2) String[] pkg2Versions = {"1.1.1", "1.1.0", "2.0.0"}; for (String ver : pkg2Versions) { // Update package version, pack and push editVersionBallerinaToml(this.tempWorkspaceDirectory.resolve("pkg2"), ver); Process p = packTrigger("pkg2", this.tempWorkspaceDirectory, this.envVariables); String ignoredPackOut = getString(p.getInputStream()); p = pushTrigger("pkg2", this.tempWorkspaceDirectory, this.envVariables); String pushOutput = getString(p.getInputStream()); // Normalize output so Windows backslashes don't break the assertion String normalizedPushOutput = pushOutput.replace("\\", "/") .replace("\r\n", "\n").replace("\r", "\n"); String collapsedPush = normalizedPushOutput.replaceAll("\\s+", " "); String expectedPushMsg2 = "Successfully pushed target/bala/" + org + "-pkg2-any-" + ver + ".bala to '" + GITHUB_REPO_ID + "' repository."; Assert.assertTrue(collapsedPush.contains(expectedPushMsg2), "Expected push success message. Actual output: " + pushOutput); } } @Test(description = "Build package with soft locking mode", dependsOnMethods = "testCase2_publishAdditionalVersionsForDeps", groups = "testCase2") public void testCase2_1_softLockingMode_resolvesLatestCompatible() throws IOException, InterruptedException { List args = new ArrayList<>(); File dependencyPathBefore = this.tempWorkspaceDirectory.resolve("myproject1") .resolve("Dependencies.toml").toFile(); if (dependencyPathBefore.exists()) { if (!dependencyPathBefore.delete()) { Assert.fail("Could not delete Dependencies.toml at " + dependencyPathBefore.getAbsolutePath()); } List cleanArgs = new ArrayList<>(); Process clean = executeCleanCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve("myproject1"), cleanArgs, this.envVariables); String cleanErrors = getString(clean.getErrorStream()); if (!cleanErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + cleanErrors); } } args.add("--locking-mode=soft"); Process build = executeBuildCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve("myproject1"), args, this.envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(build.getInputStream()); String normalized = buildOutput.replace("\\", "/") .replace("\r\n", "\n").replace("\r", "\n"); String collapsed = normalized.replaceAll("\\s+", " "); Assert.assertTrue(collapsed.contains("Generating executable") && collapsed.contains("target/bin/myproject1.jar"), "Expected generating executable + target/bin/myproject1.jar in output. Actual output: " + buildOutput); Path dependencyPath = this.tempWorkspaceDirectory.resolve("myproject1").resolve("Dependencies.toml"); Optional pkg1Version = MavenCustomRepoTestUtils.getPackageVersionFromDependencies(dependencyPath, "pkg1"); Optional pkg2Version = MavenCustomRepoTestUtils.getPackageVersionFromDependencies(dependencyPath, "pkg2"); Assert.assertTrue(pkg1Version.isPresent(), "Expected pkg1 to be present in Dependencies.toml"); Assert.assertEquals(pkg1Version.get(), "0.1.1", "Package version is not matching with " + "the expected package version"); Assert.assertTrue(pkg2Version.isPresent(), "Expected pkg2 to be present in Dependencies.toml"); Assert.assertEquals(pkg2Version.get(), "1.1.1", "Package version is not matching with " + "the expected package version"); } @Test(description = "Build package with medium locking mode", dependsOnMethods = "testCase2_publishAdditionalVersionsForDeps", groups = "testCase2") public void testCase2_2_mediumLockingMode_resolvesConservative() throws IOException, InterruptedException { List args = new ArrayList<>(); File dependencyPathBefore = this.tempWorkspaceDirectory.resolve("myproject1").resolve("Dependencies.toml").toFile(); if (dependencyPathBefore.exists()) { if (!dependencyPathBefore.delete()) { Assert.fail("Could not delete Dependencies.toml at " + dependencyPathBefore.getAbsolutePath()); } List cleanArgs = new ArrayList<>(); Process clean = executeCleanCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve("myproject1"), cleanArgs, this.envVariables); String cleanErrors = getString(clean.getErrorStream()); if (!cleanErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + cleanErrors); } } args.add("--locking-mode=medium"); Process build = executeBuildCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve("myproject1"), args, this.envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(build.getInputStream()); String normalized = buildOutput.replace("\\", "/") .replace("\r\n", "\n").replace("\r", "\n"); String collapsed = normalized.replaceAll("\\s+", " "); Assert.assertTrue(collapsed.contains("Generating executable") && collapsed.contains("target/bin/myproject1.jar"), "Expected generating executable + " + "target/bin/myproject1.jar in output. Actual output: " + buildOutput); Path dependencyPath = this.tempWorkspaceDirectory.resolve("myproject1").resolve("Dependencies.toml"); Optional pkg1Version = MavenCustomRepoTestUtils.getPackageVersionFromDependencies(dependencyPath, "pkg1"); Optional pkg2Version = MavenCustomRepoTestUtils.getPackageVersionFromDependencies(dependencyPath, "pkg2"); Assert.assertTrue(pkg1Version.isPresent(), "Expected pkg1 to be present in Dependencies.toml"); Assert.assertEquals(pkg1Version.get(), "0.1.1", "Package version is not matching with the " + "expected package version"); Assert.assertTrue(pkg2Version.isPresent(), "Expected pkg2 to be present in Dependencies.toml"); Assert.assertEquals(pkg2Version.get(), "1.0.0", "Package version is not matching with the " + "expected package version"); } // HARD mode enforces exact compiler update reproducibility. // Build is expected to fail if the project was built using a different Swan Lake update. @Test(description = "Build package with hard locking mode", dependsOnMethods = "testCase2_publishAdditionalVersionsForDeps", groups = "testCase2") public void testCase2_3_hardLockingMode_enforcesExact() throws IOException, InterruptedException { List args = new ArrayList<>(); File dependencyPathBefore = this.tempWorkspaceDirectory.resolve("myproject1") .resolve("Dependencies.toml").toFile(); if (dependencyPathBefore.exists()) { if (!dependencyPathBefore.delete()) { Assert.fail("Could not delete Dependencies.toml at " + dependencyPathBefore.getAbsolutePath()); } List cleanArgs = new ArrayList<>(); Process clean = executeCleanCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve("myproject1"), cleanArgs, this.envVariables); String cleanErrors = getString(clean.getErrorStream()); if (!cleanErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + cleanErrors); } } args.add("--locking-mode=hard"); Process build = executeBuildCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve("myproject1"), args, this.envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(build.getInputStream()); String normalized = buildOutput.replace("\\", "/") .replace("\r\n", "\n").replace("\r", "\n"); String collapsed = normalized.replaceAll("\\s+", " "); Assert.assertTrue(collapsed.contains("Generating executable") && collapsed.contains("target/bin/myproject1.jar"), "Expected generating executable + target/bin/myproject1.jar in output. Actual output: " + buildOutput); Path dependencyPath = this.tempWorkspaceDirectory.resolve("myproject1").resolve("Dependencies.toml"); Optional pkg1Version = MavenCustomRepoTestUtils.getPackageVersionFromDependencies(dependencyPath, "pkg1"); Optional pkg2Version = MavenCustomRepoTestUtils.getPackageVersionFromDependencies(dependencyPath, "pkg2"); Assert.assertTrue(pkg1Version.isPresent(), "Expected pkg1 to be present in Dependencies.toml"); Assert.assertEquals(pkg1Version.get(), "0.1.0", "Package version is not matching with the " + "expected package version"); Assert.assertTrue(pkg2Version.isPresent(), "Expected pkg2 to be present in Dependencies.toml"); Assert.assertEquals(pkg2Version.get(), "1.0.0", "Package version is not matching with the " + "expected package version"); } @Test(description = "Build package with locked mode", dependsOnMethods = {"testCase2_publishAdditionalVersions" + "ForDeps", "testCase2_3_hardLockingMode_enforcesExact"}, groups = "testCase2") public void testCase2_4_lockedMode_usesLockedVersions() throws IOException, InterruptedException { List args = new ArrayList<>(); args.add("--locking-mode=locked"); Process build = executeBuildCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve("myproject1"), args, this.envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(build.getInputStream()); String normalized = buildOutput.replace("\\", "/").replace("\r\n", "\n") .replace("\r", "\n"); String collapsed = normalized.replaceAll("\\s+", " "); Assert.assertTrue(collapsed.contains("Generating executable") && collapsed.contains("target/bin/myproject1.jar"), "Expected generating executable + target/bin/myproject1.jar in output. Actual output: " + buildOutput); Path dependencyPath = this.tempWorkspaceDirectory.resolve("myproject1").resolve("Dependencies.toml"); Optional pkg1Version = MavenCustomRepoTestUtils.getPackageVersionFromDependencies(dependencyPath, "pkg1"); Optional pkg2Version = MavenCustomRepoTestUtils.getPackageVersionFromDependencies(dependencyPath, "pkg2"); Assert.assertTrue(pkg1Version.isPresent(), "Expected pkg1 to be present in Dependencies.toml"); Assert.assertEquals(pkg1Version.get(), "0.1.0", "Package version is not matching with the " + "expected package version"); Assert.assertTrue(pkg2Version.isPresent(), "Expected pkg2 to be present in Dependencies.toml"); Assert.assertEquals(pkg2Version.get(), "1.0.0", "Package version is not matching with the " + "expected package version"); } @Test(description = "Build package with backwards compatibility", dependsOnGroups = "testCase2") public void testCase3_backwardCompatibility_legacyBallerinaToml() throws IOException, InterruptedException { Path dependencyPath = this.tempWorkspaceDirectory.resolve("myproject1").resolve("Dependencies.toml"); if (Files.deleteIfExists(dependencyPath)) { // Use separate argument lists for clean and build to avoid reusing/mutating the same list List cleanArgs = new ArrayList<>(); Process clean = executeCleanCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve("myproject1"), cleanArgs, this.envVariables); String cleanErrors = getString(clean.getErrorStream()); if (!cleanErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + cleanErrors); } List buildArgs = new ArrayList<>(); Process build = executeBuildCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve("myproject1"), buildArgs, this.envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(build.getInputStream()); String normalized = buildOutput.replace("\\", "/") .replace("\r\n", "\n").replace("\r", "\n"); String collapsed = normalized.replaceAll("\\s+", " "); Assert.assertTrue(collapsed.contains("Generating executable") && collapsed.contains("target/bin/myproject1.jar"), "Expected generating executable + target/bin/myproject1.jar in output. Actual output: " + buildOutput); Path dependencyPath2 = this.tempWorkspaceDirectory.resolve("myproject1") .resolve("Dependencies.toml"); Optional pkg2Version = MavenCustomRepoTestUtils.getPackageVersionFromDependencies(dependencyPath2, "pkg2"); Assert.assertTrue(pkg2Version.isPresent(), "Expected pkg2 to be present in Dependencies.toml"); Assert.assertEquals(pkg2Version.get(), "1.1.1", "Package version is not matching with " + "the expected package version"); } else { Assert.fail("Could not delete Dependencies.toml file to test backwards compatibility"); } } private void pinPkg1AndPkg2To100() throws IOException, InterruptedException { Path dependencyPath = this.tempWorkspaceDirectory.resolve("myproject1").resolve("Dependencies.toml"); if (Files.exists(dependencyPath)) { boolean deleted = Files.deleteIfExists(dependencyPath); if (!deleted) { Assert.fail("Could not delete Dependencies.toml at " + dependencyPath); } List cleanArgs = new ArrayList<>(); Process clean = executeCleanCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve("myproject1"), cleanArgs, this.envVariables); String cleanErrors = getString(clean.getErrorStream()); if (!cleanErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + cleanErrors); } } Path ballerinaTOML = this.tempWorkspaceDirectory.resolve("myproject1"); Assert.assertTrue(updateVersionForPackage(ballerinaTOML, "pkg1", "1.0.0"), "pkg1 not found in Ballerina.toml"); Assert.assertTrue(updateVersionForPackage(ballerinaTOML, "pkg2", "1.0.0"), "pkg2 not found in Ballerina.toml"); List args = new ArrayList<>(); args.add("--locking-mode=hard"); Process build = executeBuildCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve("myproject1"), args, this.envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } } // New: run this setup only before the Test Case 4 group @BeforeGroups("testCase4") public void beforeGroupTestCase4() throws IOException, InterruptedException { pinPkg1AndPkg2To100(); } @Test(description = "Test case 4.1: Add dep3 and build with SOFT locking mode", dependsOnGroups = "testCase2", groups = "testCase4") public void testCase4_1_addDep3_softLocking() throws IOException, InterruptedException { Path projectDir = this.tempWorkspaceDirectory.resolve("myproject1"); // Ensure legacy dependency for pkg3 is present (idempotent) MavenCustomRepoTestUtils.ensureLegacyDependency(projectDir, "bctestorg", "pkg3", "1.0.0", GITHUB_REPO_ID); List args = new ArrayList<>(); args.add("--locking-mode=soft"); Process build = executeBuildCommand(DISTRIBUTION_FILE_NAME, projectDir, args, this.envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(build.getInputStream()); String normalized = buildOutput.replace("\\", "/").replace("\r\n", "\n").replace("\r", "\n"); String collapsed = normalized.replaceAll("\\s+", " "); Assert.assertTrue(collapsed.contains("Generating executable") && collapsed.contains("target/bin/myproject1.jar"), "Expected generating executable + target/bin/myproject1.jar in output. Actual output: " + buildOutput); Path dependencyPath = projectDir.resolve("Dependencies.toml"); Optional pkg3Version = MavenCustomRepoTestUtils.getPackageVersionFromDependencies(dependencyPath, "pkg3"); Optional pkg2Version = MavenCustomRepoTestUtils.getPackageVersionFromDependencies(dependencyPath, "pkg2"); Assert.assertTrue(pkg2Version.isPresent(), "Expected pkg2 to be present in Dependencies.toml"); Assert.assertEquals(pkg2Version.get(), "1.1.1", "pkg2 version should be 1.1.1"); Assert.assertTrue(pkg3Version.isPresent(), "Expected pkg3 to be present in Dependencies.toml"); Assert.assertEquals(pkg3Version.get(), "1.0.0", "pkg3 version should be 1.0.0"); } @Test(description = "Test case 4.2: Add dep3 and build with MEDIUM (default) locking mode", dependsOnGroups = "testCase2", groups = "testCase4") public void testCase4_2_addDep3_mediumLocking() throws IOException, InterruptedException { Path projectDir = this.tempWorkspaceDirectory.resolve("myproject1"); // Ensure legacy dependency for pkg3 is present (idempotent) MavenCustomRepoTestUtils.pasteStaticMainBalWithPkg1AndPkg2(projectDir); // Remove existing Dependencies.toml in an idempotent way Files.deleteIfExists(projectDir.resolve("Dependencies.toml")); List args = new ArrayList<>(); args.add("--locking-mode=hard"); Process buildCleanup = executeBuildCommand(DISTRIBUTION_FILE_NAME, projectDir, args, this.envVariables); String buildCleanupErrors = getString(buildCleanup.getErrorStream()); if (!buildCleanupErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildCleanupErrors); } pasteStaticMainBalWithAllPkgs(projectDir); args = new ArrayList<>(); args.add("--locking-mode=medium"); Process build = executeBuildCommand(DISTRIBUTION_FILE_NAME, projectDir, args, this.envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(build.getInputStream()); String normalized = buildOutput.replace("\\", "/").replace("\r\n", "\n") .replace("\r", "\n"); String collapsed = normalized.replaceAll("\\s+", " "); Assert.assertTrue(collapsed.contains("Generating executable") && collapsed.contains("target/bin/myproject1.jar"), "Expected generating executable + target/bin/myproject1.jar in output. Actual output: " + buildOutput); Path dependencyPath = projectDir.resolve("Dependencies.toml"); Optional pkg2Version = MavenCustomRepoTestUtils.getPackageVersionFromDependencies(dependencyPath, "pkg2"); Optional pkg3Version = MavenCustomRepoTestUtils.getPackageVersionFromDependencies(dependencyPath, "pkg3"); Assert.assertTrue(pkg2Version.isPresent(), "Expected pkg2 to be present in Dependencies.toml"); Assert.assertEquals(pkg2Version.get(), "1.0.0", "Pkg2 version should be 1.0.0"); Assert.assertTrue(pkg3Version.isPresent(), "Expected pkg3 to be present in Dependencies.toml"); Assert.assertEquals(pkg3Version.get(), "1.0.0", "pkg3 version should be 1.0.0"); } @Test(description = "Test case 4.3: Add dep3 and build with HARD locking mode", dependsOnGroups = "testCase2", groups = "testCase4") public void testCase4_3_addDep3_hardLocking() throws IOException, InterruptedException { Path projectDir = this.tempWorkspaceDirectory.resolve("myproject1"); // Ensure legacy dependency for pkg3 is present (idempotent) MavenCustomRepoTestUtils.pasteStaticMainBalWithPkg1AndPkg2(projectDir); // Remove existing Dependencies.toml in an idempotent way Files.deleteIfExists(projectDir.resolve("Dependencies.toml")); List args = new ArrayList<>(); args.add("--locking-mode=hard"); Process buildCleanup = executeBuildCommand(DISTRIBUTION_FILE_NAME, projectDir, args, this.envVariables); String buildCleanupErrors = getString(buildCleanup.getErrorStream()); if (!buildCleanupErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildCleanupErrors); } pasteStaticMainBalWithAllPkgs(projectDir); args = new ArrayList<>(); args.add("--locking-mode=hard"); Process build = executeBuildCommand(DISTRIBUTION_FILE_NAME, projectDir, args, this.envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(build.getInputStream()); String normalized = buildOutput.replace("\\", "/").replace("\r\n", "\n") .replace("\r", "\n"); String collapsed = normalized.replaceAll("\\s+", " "); Assert.assertTrue(collapsed.contains("Generating executable") && collapsed.contains("target/bin/myproject1.jar"), "Expected generating executable + target/bin/myproject1.jar in output. Actual output: " + buildOutput); Path dependencyPath = projectDir.resolve("Dependencies.toml"); Optional pkg2Version = MavenCustomRepoTestUtils.getPackageVersionFromDependencies(dependencyPath, "pkg2"); Optional pkg3Version = MavenCustomRepoTestUtils.getPackageVersionFromDependencies(dependencyPath, "pkg3"); Assert.assertTrue(pkg2Version.isPresent(), "Expected pkg2 to be present in Dependencies.toml"); Assert.assertTrue(pkg3Version.isPresent(), "Expected pkg3 to be present in Dependencies.toml"); Assert.assertEquals(pkg2Version.get(), "1.0.0", "pkg2 version should be 1.0.0"); Assert.assertEquals(pkg3Version.get(), "1.0.0", "pkg3 version should be 1.0.0"); } @Test(description = "Test case 4.4: Add dep3 and build with LOCKED locking mode", dependsOnGroups = "testCase2", groups = "testCase4") public void testCase4_4_addDep3_lockedLocking() throws IOException, InterruptedException { Path projectDir = this.tempWorkspaceDirectory.resolve("myproject1"); MavenCustomRepoTestUtils.pasteStaticMainBalWithPkg1AndPkg2(projectDir); Files.deleteIfExists(projectDir.resolve("Dependencies.toml")); List args = new ArrayList<>(); args.add("--locking-mode=hard"); Process buildCleanup = executeBuildCommand(DISTRIBUTION_FILE_NAME, projectDir, args, this.envVariables); String buildCleanupErrors = getString(buildCleanup.getErrorStream()); if (!buildCleanupErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildCleanupErrors); } pasteStaticMainBalWithAllPkgs(projectDir); args = new ArrayList<>(); args.add("--locking-mode=locked"); Process build = executeBuildCommand(DISTRIBUTION_FILE_NAME, projectDir, args, this.envVariables); String buildErrors = getString(build.getErrorStream()); String buildOutput = getString(build.getInputStream()); String normalized = buildOutput.replace("\\", "/").replace("\r\n", "\n") .replace("\r", "\n"); String collapsed = normalized.replaceAll("\\s+", " "); // Expect a locked-mode error when attempting to add new imports. boolean stderrHasLockedMsg = buildErrors != null && (buildErrors.contains("cannot add new imports with" + " --locking-mode=locked") || buildErrors.contains("package resolution contains errors")); boolean stdoutHasLockedMsg = collapsed.contains("cannot add new imports with --locking-mode=locked") || collapsed.contains("package resolution contains errors"); Assert.assertTrue(stderrHasLockedMsg || stdoutHasLockedMsg, "Expected locked-mode error when adding new imports. stdout: " + collapsed + "\nstderr: " + buildErrors); // Build should not have generated an executable in locked mode when adding new imports. Assert.assertFalse(collapsed.contains("Generating executable") && collapsed.contains("target/bin/myproject1.jar"), "Build unexpectedly succeeded in locked locking mode when adding new imports. stdout: " + collapsed + "\nstderr: " + buildErrors); } @Test(description = "Add a new version for an existing dependency and build", dependsOnMethods = "testCase2_publishAdditionalVersionsForDeps") public void testCase5_addVersionInBallerinaToml_buildSucceeds() throws IOException, InterruptedException { // Update the package version in Ballerina.toml first, then build to pick up the change. Assert.assertTrue(updateVersionForPackage(this.tempWorkspaceDirectory.resolve("myproject1"), "pkg2", "1.1.0"), "pkg2 not found in Ballerina.toml"); List args = new ArrayList<>(); Process build = executeBuildCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve("myproject1"), args, this.envVariables); String buildErrors = getString(build.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } String buildOutput = getString(build.getInputStream()); String normalized = buildOutput.replace("\\", "/").replace("\r\n", "\n") .replace("\r", "\n"); String collapsed = normalized.replaceAll("\\s+", " "); Assert.assertTrue(collapsed.contains("Generating executable") && collapsed.contains("target/bin/myproject1.jar"), "Expected generating executable + target/bin/myproject1.jar in output. Actual output: " + buildOutput); Path dependencyPath = this.tempWorkspaceDirectory.resolve("myproject1").resolve("Dependencies.toml"); Optional pkg2Version = MavenCustomRepoTestUtils.getPackageVersionFromDependencies(dependencyPath, "pkg2"); Assert.assertTrue(pkg2Version.isPresent(), "Expected pkg2 to be present in Dependencies.toml"); Assert.assertEquals(pkg2Version.get(), "1.1.1", "Package version is not matching with the " + "expected package version"); } @Test(description = "Add an incompatible version for an existing dependency and build should fail", dependsOnMethods = "testCase2_publishAdditionalVersionsForDeps") public void testCase5_addIncompatibleVersion_buildFails() throws IOException, InterruptedException { Path dependencyPath = this.tempWorkspaceDirectory.resolve("myproject1").resolve("Dependencies.toml"); if (!Files.exists(dependencyPath)) { // If the locked Dependencies.toml was removed by an earlier test, recreate a pinned state. // pinPkg1AndPkg2To100 is idempotent and will create a hard-locked Dependencies.toml. pinPkg1AndPkg2To100(); } // Read the existing locked version so we can restore it after the test String existingVersion = MavenCustomRepoTestUtils.getPackageVersionFromDependencies(dependencyPath, "pkg2").orElse(""); // Update Ballerina.toml to an incompatible version first, then run the build and assert failure try { Assert.assertTrue(updateVersionForPackage(this.tempWorkspaceDirectory.resolve("myproject1"), "pkg2", "2.0.0"), "pkg2 not found in Ballerina.toml"); List args = new ArrayList<>(); Process build = executeBuildCommand(DISTRIBUTION_FILE_NAME, this.tempWorkspaceDirectory.resolve("myproject1"), args, this.envVariables); String buildErrors = getString(build.getErrorStream()); Assert.assertTrue(buildErrors.contains("incompatible with the version locked in Dependencies.toml"), "Incompatible version error message not found in the build errors. Actual stderr: " + buildErrors); } finally { if (!existingVersion.isEmpty()) { Assert.assertTrue(updateVersionForPackage(this.tempWorkspaceDirectory.resolve("myproject1"), "pkg2", existingVersion), "pkg2 not found when restoring version in Ballerina.toml"); } } } @AfterClass(alwaysRun = true) public void cleanup() throws IOException { deleteFiles(actualHomeDirectory, true); Files.walkFileTree(actualHomeDirectoryClone, new MavenCustomRepoTest.Copy(actualHomeDirectoryClone, actualHomeDirectory)); deleteFiles(actualHomeDirectoryClone, false); deleteFiles(tempWorkspaceDirectory, false); deleteArtifacts(org, "pkg1"); deleteArtifacts(org, "pkg2"); deleteArtifacts(org, "pkg3"); } /** * Copy test resources to temp directory. */ static class Copy extends SimpleFileVisitor { private final Path fromPath; private final Path toPath; private final StandardCopyOption copyOption; Copy(Path fromPath, Path toPath, StandardCopyOption copyOption) { this.fromPath = fromPath; this.toPath = toPath; this.copyOption = copyOption; } Copy(Path fromPath, Path toPath) { this(fromPath, toPath, StandardCopyOption.REPLACE_EXISTING); } @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { Path targetPath = toPath.resolve(fromPath.relativize(dir).toString()); if (!Files.exists(targetPath)) { Files.createDirectory(targetPath); } return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { Files.copy(file, toPath.resolve(fromPath.relativize(file).toString()), copyOption); return FileVisitResult.CONTINUE; } } } ================================================ FILE: project-api-tests/src/test/java/org/ballerina/projectapi/MavenCustomRepoTestUtils.java ================================================ /* * Copyright (c) 2023, WSO2 LLC. (http://wso2.com). * * 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.ballerina.projectapi; import io.ballerina.toml.api.Toml; import okhttp3.OkHttpClient; import okhttp3.Request; import org.testng.Assert; import java.io.BufferedReader; import java.io.File; 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.StandardOpenOption; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; import static org.ballerina.projectapi.TestUtils.DISTRIBUTION_FILE_NAME; import static org.ballerina.projectapi.TestUtils.OUTPUT_CONTAIN_ERRORS; import static org.ballerina.projectapi.TestUtils.executePackCommand; import static org.ballerina.projectapi.TestUtils.executePushCommand; /** * Utility class for maven repository tests. */ public class MavenCustomRepoTestUtils { /** * Create Settings.toml inside the home repository. * @param dirPath folder path to the settings toml * @throws IOException i/o exception when writing to file */ static void createSettingToml(Path dirPath) throws IOException { String content = "[[repository.maven]]\n " + "id = \"github1\"\n " + "url = \"https://maven.pkg.github.com/ballerina-platform/ballerina-release\"\n " + "username = \"ballerina-platform\"\n " + "accesstoken = \"" + getGithubToken() + "\"\n"; Files.write(dirPath.resolve("Settings.toml"), content.getBytes(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); } /** * Get access token of GitHub required to push the module. * * @return token required to push the module. */ private static String getGithubToken() { return System.getenv("githubAccessToken"); } /** * Get environment variables and add ballerina_home as a env variable the tmp directory. * * @return env directory variable array */ static Map getEnvVariables() { Map envVarMap = System.getenv(); Map retMap = new HashMap<>(); envVarMap.forEach(retMap::put); return retMap; } /** * Convert input stream to string. * * @param outputs input stream * @return converted string * @throws IOException Error when reading from input stream */ static String getString(InputStream outputs) throws IOException { try (BufferedReader br = new BufferedReader(new InputStreamReader(outputs))) { Stream logLines = br.lines(); String generatedLog = logLines.collect(Collectors.joining("\n")); logLines.close(); return generatedLog; } } /** * Delete files inside directories. * * @param dirPath directory path * @param deleteDirContentOnly delete only the content inside the directory * @throws IOException throw an exception if an issue occurs */ static void deleteFiles(Path dirPath, boolean deleteDirContentOnly) throws IOException { Files.walk(dirPath) .sorted(Comparator.reverseOrder()) .map(Path::toFile) .forEach(File::delete); if (deleteDirContentOnly) { Files.createDirectories(dirPath); } } /** * Delete artifacts from GitHub. * @param org organization name * @param packagename package name */ static void deleteArtifacts(String org, String packagename) throws IOException { OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url("https://api.github.com/orgs/ballerina-platform/packages/maven/" + org + "." + packagename) .header("Accept", "application/vnd.github+json") .header("Authorization", "Bearer " + getGithubToken()) .header("X-GitHub-Api-Version", "2022-11-28") .delete() .build(); client.newCall(request).execute(); } /** * Run the `ballerina pack` command for the given package and return the spawned Process. * * @param packageName the package directory name under the sourceDirectory * @param sourceDirectory the path that contains the package folders * @param envVariable environment variables to pass to the process * @return the Process for the pack command * @throws IOException if an I/O error occurs when starting the process * @throws InterruptedException if the current thread is interrupted while waiting for the process */ static Process packTrigger(String packageName, Path sourceDirectory, Map envVariable) throws IOException, InterruptedException { Process process = executePackCommand(DISTRIBUTION_FILE_NAME, sourceDirectory.resolve(packageName), new ArrayList<>(), envVariable); String buildErrors = getString(process.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } return process; } /** * Run the `ballerina push` command for the given package and return the spawned Process. * * @param packageName the package directory name under the sourceDirectory * @param sourceDirectory the path that contains the package folders * @param envVariable environment variables to pass to the process * @return the Process for the push command * @throws IOException if an I/O error occurs when starting the process * @throws InterruptedException if the current thread is interrupted while waiting for the process */ static Process pushTrigger(String packageName, Path sourceDirectory, Map envVariable) throws IOException, InterruptedException { List args = new ArrayList<>(); args.add("--repository=" + MavenCustomRepoTest.GITHUB_REPO_ID); Process process = executePushCommand(DISTRIBUTION_FILE_NAME, sourceDirectory.resolve(packageName), args, envVariable); String buildErrors = getString(process.getErrorStream()); if (!buildErrors.isEmpty()) { Assert.fail(OUTPUT_CONTAIN_ERRORS + buildErrors); } return process; } /** * Replace the `version = ...` line inside the [package] section of a Ballerina.toml. * *

The method performs a simple text substitution: any line starting with * {@code version =} is replaced with the provided version string. The file is * overwritten with the updated contents.

* * @param sourceDirectory path to the project containing Ballerina.toml * @param version the version string to set (e.g. "1.0.0") * @throws IOException if reading or writing the Ballerina.toml fails */ static void editVersionBallerinaToml(Path sourceDirectory, String version) throws IOException { Path ballerinaTomlPath = sourceDirectory.resolve("Ballerina.toml"); String toml = Files.readString(ballerinaTomlPath); int pkgStart = toml.indexOf("[package]"); if (pkgStart == -1) { // No [package] block - append one String appended = toml + System.lineSeparator() + "[package]" + System.lineSeparator() + "version = \"" + version + "\""; Files.writeString(ballerinaTomlPath, appended, StandardOpenOption.TRUNCATE_EXISTING); return; } int nextHeader = toml.indexOf("\n[", pkgStart + 1); int blockEnd = nextHeader == -1 ? toml.length() : nextHeader; String block = toml.substring(pkgStart, blockEnd); Pattern versionLine = Pattern.compile("(?m)^(\\s*)version\\s*=.*$"); Matcher vm = versionLine.matcher(block); String newBlock; if (vm.find()) { // replace existing version line, preserve indentation newBlock = vm.replaceFirst("$1version = \"" + version + "\""); } else { // insert version after header line int headerEnd = block.indexOf('\n'); if (headerEnd == -1) { newBlock = block + System.lineSeparator() + "version = \"" + version + "\""; } else { newBlock = block.substring(0, headerEnd + 1) + "version = \"" + version + "\"" + (headerEnd + 1 < block.length() ? "\n" + block.substring(headerEnd + 1) : ""); } } String updated = toml.substring(0, pkgStart) + newBlock + toml.substring(blockEnd); Files.writeString(ballerinaTomlPath, updated, StandardOpenOption.TRUNCATE_EXISTING); } /** * Update (or insert) a `version = "..."` entry for the given package name inside a * Ballerina.toml located in the project directory. * *

The helper searches for a line containing {@code name = ""} and then * replaces an existing {@code version =} line following it, or inserts one if missing. * The operation is persisted to disk and the method returns whether a change was made.

* * @param sourceDirectory the project directory that contains Ballerina.toml * @param packageName the package name to adjust (e.g. "pkg1") * @param version the version string to set (e.g. "1.1.0") * @return true if the file was modified, false if the package name was not found * @throws IOException if reading or writing the file fails **/ static boolean updateVersionForPackage(Path sourceDirectory, String packageName, String version) throws IOException { Path ballerinaTomlPath = sourceDirectory.resolve("Ballerina.toml"); List lines = Files.readAllLines(ballerinaTomlPath); for (int i = 0; i < lines.size(); i++) { String trimmed = lines.get(i).trim(); // look for name = "pkg" if (trimmed.startsWith("name") && trimmed.contains("\"" + packageName + "\"")) { // search forward for version line until next section header for (int j = i + 1; j < lines.size(); j++) { String t = lines.get(j).trim(); if (t.startsWith("[")) { // reached next section, insert version right after the name line lines.add(i + 1, "version = \"" + version + "\""); Files.write(ballerinaTomlPath, lines); return true; } if (t.startsWith("version")) { // replace existing version line lines.set(j, "version = \"" + version + "\""); Files.write(ballerinaTomlPath, lines); return true; } } // reached EOF without finding version or next section: append version after name lines.add(i + 1, "version = \"" + version + "\""); Files.write(ballerinaTomlPath, lines); return true; } } return false; } /** * Read a Dependencies.toml and return the version string for the given package name if present. * * @param dependencyTomlPath path to Dependencies.toml * @param packageName package name to look up * @return Optional version string * @throws IOException when reading the toml fails */ static Optional getPackageVersionFromDependencies(Path dependencyTomlPath, String packageName) throws IOException { Toml toml = Toml.read(dependencyTomlPath); List packages = toml.getTables("package"); return packages.stream() .filter(pkg -> pkg.get("name").map(n -> n.toString()).orElse("").equals(packageName)) .map(pkg -> pkg.get("version").map(v -> v.toString())) .filter(Optional::isPresent) .map(Optional::get) .findFirst(); } /** * Add or update a [[dependency]] in Ballerina.toml for the given org and package, then update the source. * * @param projectDir the project directory containing Ballerina.toml * @param org the organization/group id of the dependency (e.g., "bctestorg") * @param name the package name of the dependency (e.g., "pkg1") * @param version the version to pin for the dependency (e.g., "1.0.0") * @param repository the repository id to use for the dependency (e.g., "github1") * @throws IOException if reading or writing the Ballerina.toml fails */ public static void ensureLegacyDependency(Path projectDir, String org, String name, String version, String repository) throws IOException { Path ballerinaTomlPath = projectDir.resolve("Ballerina.toml"); if (!Files.exists(ballerinaTomlPath)) { throw new IOException("Missing Ballerina.toml in project: " + projectDir); } String toml = Files.readString(ballerinaTomlPath); String desiredDepBlock = "\n[[dependency]]\norg=\"" + org + "\"\nname=\"" + name + "\"\nversion=\"" + version + "\"\nrepository=\"" + repository + "\"\n"; // Find an existing [[dependency]] block for the given org+name Pattern depPattern = Pattern.compile("(?is)\\[\\[dependency\\]\\].*?org\\s*=\\s*\"" + Pattern.quote(org) + "\".*?name\\s*=\\s*\"" + Pattern.quote(name) + "\"" + ".*?(?=\\z|\\[\\[dependency\\]\\])"); Matcher matcher = depPattern.matcher(toml); if (matcher.find()) { String existingBlock = matcher.group(0); // Try to extract version if present Pattern verPat = Pattern.compile("version\\s*=\\s*\"([^\"]*)\"", Pattern.CASE_INSENSITIVE); Matcher vm = verPat.matcher(existingBlock); if (vm.find()) { String existingVersion = vm.group(1); if (existingVersion.equals(version)) { // already the desired version // Ensure source-level usage and return if ("pkg1".equals(name) || "pkg2".equals(name) || "pkg3".equals(name)) { pasteStaticMainBalWithAllPkgs(projectDir); } return; } } // Version differs or missing -> replace the whole dependency block with the desired block String updatedToml = toml.substring(0, matcher.start()) + desiredDepBlock + toml.substring(matcher.end()); Files.writeString(ballerinaTomlPath, updatedToml); } else { // No existing dependency block -> append Files.writeString(ballerinaTomlPath, toml + desiredDepBlock); } // Ensure source-level usage so the package becomes a compile-time dependency if ("pkg1".equals(name) || "pkg2".equals(name) || "pkg3".equals(name)) { pasteStaticMainBalWithAllPkgs(projectDir); } } /** * Writes a deterministic `main.bal` that imports pkg2 and pkg1 and calls pkg2:main() then pkg1:main(). * it returns early if the file already contains the imports and calls. * * @param projectDir the project directory where main.bal should be written * @throws IOException if an I/O error occurs while creating or writing the file */ public static void pasteStaticMainBalWithPkg1AndPkg2(Path projectDir) throws IOException { Path mainBal = projectDir.resolve("main.bal"); if (!Files.exists(mainBal)) { mainBal = projectDir.resolve("src").resolve("main.bal"); } if (mainBal.getParent() != null && !Files.exists(mainBal.getParent())) { Files.createDirectories(mainBal.getParent()); } String import1 = "import bctestorg/pkg1;"; String import2 = "import bctestorg/pkg2;"; String call1 = "pkg1:main();"; String call2 = "pkg2:main();"; String snippet = import2 + "\n" + import1 + "\n" + "\n" + "public function main(string... args) {\n" + " // Ensure pkg2 then pkg1 are used so they become compile-time dependencies\n" + " " + call2 + "\n" + " " + call1 + "\n" + "}\n"; Files.writeString(mainBal, snippet, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); } /** * Writes a single deterministic `main.bal` that uses pkg2, pkg1 and pkg3. * it returns early if the file already contains the imports and calls. * * @param projectDir the project directory where main.bal should be written * @throws IOException if an I/O error occurs while creating or writing the file */ public static void pasteStaticMainBalWithAllPkgs(Path projectDir) throws IOException { Path mainBal = projectDir.resolve("main.bal"); if (!Files.exists(mainBal)) { mainBal = projectDir.resolve("src").resolve("main.bal"); } if (mainBal.getParent() != null && !Files.exists(mainBal.getParent())) { Files.createDirectories(mainBal.getParent()); } String import1 = "import bctestorg/pkg1;"; String import2 = "import bctestorg/pkg2;"; String import3 = "import bctestorg/pkg3;"; String call1 = "pkg1:main();"; String call2 = "pkg2:main();"; String call3 = "pkg3:main();"; if (Files.exists(mainBal)) { String existing = Files.readString(mainBal); if (existing.contains(import1) && existing.contains(import2) && existing.contains(import3) && existing.contains(call1) && existing.contains(call2) && existing.contains(call3)) { return; // already contains desired snippet } } String snippet = import2 + "\n" + import1 + "\n" + import3 + "\n" + "\n" + "public function main(string... args) {\n" + " // Ensure pkg2, pkg1 and pkg3 are used so they become compile-time dependencies\n" + " " + call2 + "\n" + " " + call1 + "\n" + " " + call3 + "\n" + "}\n"; Files.writeString(mainBal, snippet, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); } } ================================================ FILE: project-api-tests/src/test/java/org/ballerina/projectapi/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.ballerina.projectapi; import net.lingala.zip4j.ZipFile; import net.lingala.zip4j.exception.ZipException; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import java.io.IOException; import java.io.PrintStream; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; import java.util.Map; import static org.ballerina.projectapi.CentralTestUtils.BALLERINA_HOME_DIR; import static org.ballerina.projectapi.CentralTestUtils.TEST_MODE_ACTIVE; /** * Utility class for tests. */ public class TestUtils { private TestUtils() { } private static final PrintStream OUT = System.out; private static final Path TARGET_DIR = Paths.get(System.getProperty("target.dir")); private static final Path TEST_DISTRIBUTION_PATH = TARGET_DIR.resolve("test-distribution"); public static final Path MAVEN_VERSION = Paths.get(System.getProperty("maven.version")); public static final Path DISTRIBUTIONS_DIR = Paths.get(System.getProperty("distributions.dir")); public static final Path CODE_NAME = Paths.get(System.getProperty("code.name")); public static final String OUTPUT_CONTAIN_ERRORS = "build output contain errors:"; public static final String DISTRIBUTION_FILE_NAME = "ballerina-" + MAVEN_VERSION + "-" + CODE_NAME; /** * Execute ballerina 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 inputream with log outputs * @throws IOException Error executing build command. * @throws InterruptedException Interrupted error executing build command. */ public static Process executeCommand(String command, String distributionName, Path sourceDirectory, List args, Map envProperties) throws IOException, InterruptedException { args.add(0, command); // Use platform-specific bal executable: on Windows use bal.bat, otherwise use bal String osName = System.getProperty("os.name"); String balExecutable = "bal"; if (osName != null && osName.toLowerCase().contains("win")) { balExecutable = "bal.bat"; } args.add(0, TEST_DISTRIBUTION_PATH.resolve(distributionName).resolve("bin").resolve(balExecutable).toString()); OUT.println("Executing: " + StringUtils.join(args, ' ')); ProcessBuilder pb = new ProcessBuilder(args); // Add env variables if (envProperties != null) { Map env = pb.environment(); for (Map.Entry entry : envProperties.entrySet()) { env.put(entry.getKey(), entry.getValue()); } } pb.directory(sourceDirectory.toFile()); Process process = pb.start(); int exitCode = process.waitFor(); return process; } public static Process executeBuildCommand(String distributionName, Path sourceDirectory, List args, Map envProperties) throws IOException, InterruptedException { return executeCommand("build", distributionName, sourceDirectory, args, envProperties); } public static Process executePackCommand(String distributionName, Path sourceDirectory, List args, Map envProperties) throws IOException, InterruptedException { return executeCommand("pack", distributionName, sourceDirectory, args, envProperties); } public static Process executePushCommand(String distributionName, Path sourceDirectory, List args, Map envProperties) throws IOException, InterruptedException { return executeCommand("push", distributionName, sourceDirectory, args, envProperties); } public static Process executePullCommand(String distributionName, Path sourceDirectory, List args, Map envProperties) throws IOException, InterruptedException { return executeCommand("pull", distributionName, sourceDirectory, args, envProperties); } public static Process executeSearchCommand(String distributionName, Path sourceDirectory, List args, Map envProperties) throws IOException, InterruptedException { return executeCommand("search", distributionName, sourceDirectory, args, envProperties); } public static Process executeToolCommand(String distributionName, Path sourceDirectory, List args, Map envProperties) throws IOException, InterruptedException { return executeCommand("tool", distributionName, sourceDirectory, args, envProperties); } public static Process executeHelpCommand(String distributionName, Path sourceDirectory, List args, Map envProperties) throws IOException, InterruptedException { return executeCommand("help", distributionName, sourceDirectory, args, envProperties); } public static Process executeCleanCommand(String distributionName, Path sourceDirectory, List args, Map envProperties) throws IOException, InterruptedException { return executeCommand("clean", distributionName, sourceDirectory, args, envProperties); } public static Process executeNewCommand(String distributionName, Path sourceDirectory, List args, Map envProperties) throws IOException, InterruptedException { return executeCommand("new", distributionName, sourceDirectory, args, envProperties); } /** * Clean and setup the distribution. * * @throws IOException */ static void setupDistributions() throws IOException { TestUtils.cleanDistribution(); TestUtils.prepareDistribution(DISTRIBUTIONS_DIR.resolve(DISTRIBUTION_FILE_NAME + ".zip")); } /** * 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 environment variables and add ballerina_home as a env variable the tmp directory. * * @return env directory variable array */ static Map addEnvVariables(Map envVariables, Path tempHomeDirectory) { envVariables.put(BALLERINA_HOME_DIR, tempHomeDirectory.toString()); envVariables.put(TEST_MODE_ACTIVE, "true"); return envVariables; } } ================================================ FILE: project-api-tests/src/test/resources/bal-tool/DistTestCommand/build.gradle ================================================ plugins { id 'java' } group 'org.example' version '1.0.0' repositories { mavenLocal() maven { url = 'https://maven.wso2.org/nexus/content/repositories/releases/' } maven { url = 'https://maven.wso2.org/nexus/content/groups/wso2-public/' } maven { url = 'https://repo.maven.apache.org/maven2' } maven { url = 'https://maven.pkg.github.com/ballerina-platform/*' credentials { username System.getenv("packageUser") password System.getenv("packagePAT") } } } dependencies { implementation 'org.ballerinalang:ballerina-cli:2201.8.0-SNAPSHOT' implementation 'info.picocli:picocli:4.0.1' implementation group: 'org.talend.sdk.component', name: 'sample', version: '10.57.0' } test { useJUnitPlatform() } ================================================ FILE: project-api-tests/src/test/resources/bal-tool/DistTestCommand/gradlew ================================================ #!/bin/sh # # Copyright © 2015-2021 the original 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 # # https://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 POSIX generated by Gradle. # # Important for running: # # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is # noncompliant, but you have some other compliant shell such as ksh or # bash, then to run this script, type that shell name before the whole # command line, like: # # ksh Gradle # # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: # * functions; # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», # «${var#prefix}», «${var%suffix}», and «$( cmd )»; # * compound commands having a testable exit status, especially «case»; # * various built-in commands including «command», «set», and «ulimit». # # Important for patching: # # (2) This script targets any POSIX shell, so it avoids extensions provided # by Bash, Ksh, etc; in particular arrays are avoided. # # The "traditional" practice of packing multiple parameters into a # space-separated string is a well documented source of bugs and security # problems, so this is (mostly) avoided, by progressively accumulating # options in "$@", and eventually passing that to Java. # # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; # see the in-line comments for details. # # There are tweaks for specific operating systems such as AIX, CygWin, # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. # ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link app_path=$0 # Need this for daisy-chained symlinks. while APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path [ -h "$app_path" ] do ls=$( ls -ld "$app_path" ) link=${ls#*' -> '} case $link in #( /*) app_path=$link ;; #( *) app_path=$APP_HOME$link ;; esac done APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" APP_BASE_NAME=${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 "$*" } >&2 die () { echo echo "$*" echo exit 1 } >&2 # 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 ;; #( MSYS* | 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" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi # Collect all arguments for the java command, stacking in reverse order: # * args from the command line # * the main class name # * -classpath # * -D...appname settings # * --module-path (only if needed) # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) # Now convert the arguments - kludge to limit ourselves to /bin/sh for arg do if case $arg in #( -*) false ;; # don't mess with options #( /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath [ -e "$t" ] ;; #( *) false ;; esac then arg=$( cygpath --path --ignore --mixed "$arg" ) fi # Roll the args list around exactly as many times as the number of # args, so each arg winds up back in the position where it started, but # possibly modified. # # NB: a `for` loop captures its iteration list before it begins, so # changing the positional parameters here affects neither the number of # iterations, nor the values presented in `arg`. shift # remove old arg set -- "$@" "$arg" # push replacement arg done fi # Collect all arguments for the java command; # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # shell script including quotes and variable substitutions, so put them in # double quotes to make sure that they get re-expanded; and # * put everything else in single quotes, so that it's not re-expanded. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ org.gradle.wrapper.GradleWrapperMain \ "$@" # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. # # In Bash we could simply go: # # readarray ARGS < <( xargs -n1 <<<"$var" ) && # set -- "${ARGS[@]}" "$@" # # but POSIX shell has neither arrays nor command substitution, so instead we # post-process each arg (as a line of input to sed) to backslash-escape any # character that might be a shell metacharacter, then use eval to reverse # that process (while maintaining the separation between arguments), and wrap # the whole thing up as a single "set" statement. # # This will of course break if any of these variables contains a newline or # an unmatched quote. # eval "set -- $( printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | xargs -n1 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | tr '\n' ' ' )" '"$@"' exec "$JAVACMD" "$@" ================================================ FILE: project-api-tests/src/test/resources/bal-tool/DistTestCommand/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 https://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 Resolve any "." and ".." in APP_HOME to make it shorter. for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi @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 execute 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 execute 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 :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 %* :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: project-api-tests/src/test/resources/bal-tool/DistTestCommand/settings.gradle ================================================ rootProject.name = 'DistTestCommand' include 'src:main:main' findProject(':src:main:main')?.name = 'main' include 'untitled' include 'main' ================================================ FILE: project-api-tests/src/test/resources/bal-tool/DistTestCommand/src/main/java/disttest/cli/DistTestCommand.java ================================================ package disttest.cli; import io.ballerina.cli.BLauncherCmd; import picocli.CommandLine; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintStream; import java.nio.charset.StandardCharsets; import java.util.List; @CommandLine.Command(name = "disttest", description = "The tool implementation for ballerina distribution tool tests") public class DistTestCommand implements BLauncherCmd { private final PrintStream printStream; @CommandLine.Option(names = {"--help", "-h", "?"}, usageHelp = true) private boolean helpFlag; @CommandLine.Parameters(description = "User name") private List argList; public DistTestCommand() { this.printStream = System.out; } public DistTestCommand(PrintStream printStream) { this.printStream = printStream; } @Override public void execute() { if (helpFlag) { StringBuilder out = new StringBuilder(); appendHelpText(out); printStream.println(out); return; } if (argList == null || argList.size() < 1) { printStream.println("dist test command 1.0.0 is executing with no args\n"); return; } printStream.println("dist test command 1.0.0 is executing with args " + argList.get(0) + "\n"); } @Override public String getName() { return "disttest"; } @Override public void printLongDesc(StringBuilder out) { appendHelpText(out); } @Override public void printUsage(StringBuilder out) { out.append("A sample tool built for testing bal tool functionality"); } @Override public void setParentCmdParser(CommandLine parentCmdParser) { } private void appendHelpText(StringBuilder out) { Class clazz = DistTestCommand.class; ClassLoader classLoader = clazz.getClassLoader(); InputStream inputStream = classLoader.getResourceAsStream("disttest.help"); if (inputStream != null) { try (InputStreamReader inputStreamREader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); BufferedReader br = new BufferedReader(inputStreamREader)) { String content = br.readLine(); out.append(content); while ((content = br.readLine()) != null) { out.append('\n').append(content); } } catch (IOException e) { out.append("Helper text is not available."); } } } } ================================================ FILE: project-api-tests/src/test/resources/bal-tool/DistTestCommand/src/main/resources/META-INF/services/io.ballerina.cli.BLauncherCmd ================================================ disttest.cli.DistTestCommand ================================================ FILE: project-api-tests/src/test/resources/bal-tool/DistTestCommand/src/main/resources/disttest.help ================================================ Sample tool used for testing the bal tools in ballerina distribution tests. bal disttest --args-- The argument to be printed. version: 1.0.0 ================================================ FILE: project-api-tests/src/test/resources/bal-tool/cmd-outputs/tool-execute-general-help.txt ================================================ Tool Commands: disttest The tool implementation for ballerina distribution tool tests ================================================ FILE: project-api-tests/src/test/resources/bal-tool/cmd-outputs/tool-execute-specific-help-1.0.0.txt ================================================ Sample tool used for testing the bal tools in ballerina distribution tests. bal disttest --args-- The argument to be printed. version: 1.0.0 ================================================ FILE: project-api-tests/src/test/resources/bal-tool/cmd-outputs/tool-execute-specific-help-1.1.0.txt ================================================ Sample tool used for testing the bal tools in ballerina distribution tests. bal disttest --args-- The argument to be printed. version: 1.1.0 ================================================ FILE: project-api-tests/src/test/resources/bal-tool/cmd-outputs/tool-execute-tool.txt ================================================ dist test command 1.1.0 is executing with args arg1 ================================================ FILE: project-api-tests/src/test/resources/bal-tool/cmd-outputs/tool-execute-unknown-cmd-non-existing.txt ================================================ ballerina: unknown command 'disttest2' Run 'bal help' for usage. ================================================ FILE: project-api-tests/src/test/resources/bal-tool/cmd-outputs/tool-execute-unknown-cmd.txt ================================================ ballerina: unknown command 'disttest' Run 'bal help' for usage. ================================================ FILE: project-api-tests/src/test/resources/bal-tool/cmd-outputs/tool-list-with-multiple-tool-versions.txt ================================================ |TOOL ID |VERSION |REPO | |----------------------|----------------|-----------| |disttest | 1.1.0 |central | |disttest |* 1.0.0 |central | ================================================ FILE: project-api-tests/src/test/resources/bal-tool/cmd-outputs/tool-list-with-no-tools.txt ================================================ no tools found locally. ================================================ FILE: project-api-tests/src/test/resources/bal-tool/cmd-outputs/tool-pull-again-with-specific-version.txt ================================================ tool 'disttest:1.0.0' is already available locally. tool 'disttest:1.0.0' is already active. ================================================ FILE: project-api-tests/src/test/resources/bal-tool/cmd-outputs/tool-pull-again-without-version.txt ================================================ tool 'disttest:1.1.0' is already available locally. tool 'disttest:1.1.0' is already active. ================================================ FILE: project-api-tests/src/test/resources/bal-tool/cmd-outputs/tool-pull-with-incompatible-dist.txt ================================================ ballerina: tool 'disttest:1.0.4' is not compatible with the current Ballerina distribution ================================================ FILE: project-api-tests/src/test/resources/bal-tool/cmd-outputs/tool-pull-with-non-existing-version.txt ================================================ unexpected error occurred while pulling tool:error: tool not found for: disttest:1.0.3 ================================================ FILE: project-api-tests/src/test/resources/bal-tool/cmd-outputs/tool-pull-with-specific-version.txt ================================================ tool 'disttest:1.0.0' pulled successfully. tool 'disttest:1.0.0' successfully set as the active version. ================================================ FILE: project-api-tests/src/test/resources/bal-tool/cmd-outputs/tool-pull-without-version.txt ================================================ tool 'disttest:1.1.0' pulled successfully. tool 'disttest:1.1.0' successfully set as the active version. ================================================ FILE: project-api-tests/src/test/resources/bal-tool/cmd-outputs/tool-remove-active-version.txt ================================================ ballerina: cannot remove active tool 'disttest:1.1.0'. ================================================ FILE: project-api-tests/src/test/resources/bal-tool/cmd-outputs/tool-remove-all.txt ================================================ tool 'disttest' successfully removed. ================================================ FILE: project-api-tests/src/test/resources/bal-tool/cmd-outputs/tool-remove-non-existing-tool.txt ================================================ ballerina: tool 'disttest2' not found. ================================================ FILE: project-api-tests/src/test/resources/bal-tool/cmd-outputs/tool-remove-non-existing-version.txt ================================================ ballerina: tool 'disttest:1.0.3' not found. ================================================ FILE: project-api-tests/src/test/resources/bal-tool/cmd-outputs/tool-remove-specific-version.txt ================================================ tool 'disttest:1.0.0' successfully removed. ================================================ FILE: project-api-tests/src/test/resources/bal-tool/cmd-outputs/tool-remove-with-incompatible-dist.txt ================================================ ballerina: tool 'disttest:1.0.4' is not compatible with the current Ballerina distribution ================================================ FILE: project-api-tests/src/test/resources/bal-tool/cmd-outputs/tool-search-with-tool-id.txt ================================================ Ballerina Central ================= |ID |PACKAGE |DESCRIPTION |DATE |VERSION | |------------|-------------|-------------|----------------|-------------| |disttest |bctestorg... |Sample to... | ================================================ FILE: project-api-tests/src/test/resources/bal-tool/cmd-outputs/tool-update-non-existing.txt ================================================ ballerina: tool 'disttest2' is not installed. ================================================ FILE: project-api-tests/src/test/resources/bal-tool/cmd-outputs/tool-update-with-new-patch-and-minor.txt ================================================ tool 'disttest:1.1.0' pulled successfully. tool 'disttest:1.1.0' successfully set as the active version. ================================================ FILE: project-api-tests/src/test/resources/bal-tool/cmd-outputs/tool-update-with-no-new-version.txt ================================================ tool 'disttest' is already up-to-date. ================================================ FILE: project-api-tests/src/test/resources/bal-tool/cmd-outputs/tool-use-active-version.txt ================================================ tool 'disttest:1.0.0' is the current active version. ================================================ FILE: project-api-tests/src/test/resources/bal-tool/cmd-outputs/tool-use-new-version.txt ================================================ tool 'disttest:1.1.0' successfully set as the active version. ================================================ FILE: project-api-tests/src/test/resources/bal-tool/cmd-outputs/tool-use-non-existent-version.txt ================================================ tool 'disttest:1.0.3' is not found. Run 'bal tool pull disttest:1.0.3' to fetch and set as the active version. ================================================ FILE: project-api-tests/src/test/resources/bal-tool/cmd-outputs/tool-use-old-version.txt ================================================ tool 'disttest:1.0.0' successfully set as the active version. ================================================ FILE: project-api-tests/src/test/resources/bal-tool/cmd-outputs/tool-use-with-incompatible-dist.txt ================================================ ballerina: tool 'disttest:1.0.4' is not compatible with the current Ballerina distribution ================================================ FILE: project-api-tests/src/test/resources/bal-tool/v1.0.0/disttestpackage/BalTool.toml ================================================ [tool] id="disttest" [[dependency]] path="./resources/DistTestCommand-1.0.0.jar" ================================================ FILE: project-api-tests/src/test/resources/bal-tool/v1.0.0/disttestpackage/Ballerina.toml ================================================ [package] org = "bctestorg" name = "disttestpackage" version = "1.0.0" ================================================ FILE: project-api-tests/src/test/resources/bal-tool/v1.0.0/disttestpackage/README.md ================================================ # disttest ## Description Sample tool used for testing the bal tools in ballerina distribution tests. ================================================ FILE: project-api-tests/src/test/resources/bal-tool/v1.0.1/disttestpackage/BalTool.toml ================================================ [tool] id="disttest" [[dependency]] path="./resources/DistTestCommand-1.0.1.jar" ================================================ FILE: project-api-tests/src/test/resources/bal-tool/v1.0.1/disttestpackage/Ballerina.toml ================================================ [package] org = "bctestorg" name = "disttestpackage" version = "1.0.1" readme = "Package.md" ================================================ FILE: project-api-tests/src/test/resources/bal-tool/v1.0.1/disttestpackage/Package.md ================================================ # disttest ## Description Sample tool used for testing the bal tools in ballerina distribution tests. ================================================ FILE: project-api-tests/src/test/resources/bal-tool/v1.0.4/disttestpackage/BalTool.toml ================================================ [tool] id="disttest" [[dependency]] path="./resources/DistTestCommand-1.0.4.jar" ================================================ FILE: project-api-tests/src/test/resources/bal-tool/v1.0.4/disttestpackage/Ballerina.toml ================================================ [package] org = "bctestorg" name = "disttestpackage" version = "1.0.4" ================================================ FILE: project-api-tests/src/test/resources/bal-tool/v1.0.4/disttestpackage/README.md ================================================ # disttest ## Description Sample tool used for testing the bal tools in ballerina distribution tests. ================================================ FILE: project-api-tests/src/test/resources/bal-tool/v1.1.0/disttestpackage/BalTool.toml ================================================ [tool] id="disttest" [[dependency]] path="./resources/DistTestCommand-1.1.0.jar" ================================================ FILE: project-api-tests/src/test/resources/bal-tool/v1.1.0/disttestpackage/Ballerina.toml ================================================ [package] org = "bctestorg" name = "disttestpackage" version = "1.1.0" ================================================ FILE: project-api-tests/src/test/resources/bal-tool/v1.1.0/disttestpackage/README.md ================================================ # disttest ## Description Sample tool used for testing the bal tools in ballerina distribution tests. ================================================ FILE: project-api-tests/src/test/resources/build-time/Project1/Ballerina.toml ================================================ [package] org = "testorg" name = "project1" version = "0.1.0" ================================================ FILE: project-api-tests/src/test/resources/build-time/Project1/main.bal ================================================ import ballerina/io; import ballerina/random; import ballerina/uuid; import ballerina/http as _; import ballerina/log as _; import ballerina/file as _; import ballerina/graphql as _; public function main() returns error? { // Generates a random decimal number between 0.0 and 1.0. float randomDecimal = random:createDecimal(); io:println("Random decimal number: ", randomDecimal); // Generates a random number between the given start(inclusive) and end(exclusive) values. int randomInteger = check random:createIntInRange(1, 100); io:println("Random integer number in range: ", randomInteger); return; } public function uuidGen() returns error? { // Tests a string to see if it is a valid UUID. boolean valid = uuid:validate("4397465e-35f9-11eb-adc1-0242ac120002"); io:println("UUID validated: ", valid.toString()); // Detects the RFC version of a UUID. uuid:Version v = check uuid:getVersion( "4397465e-35f9-11eb-adc1-0242ac120002"); io:println("UUID version: ", v.toString()); // Converts a UUID string to an array of bytes. byte[] uuidBytes1 = check uuid:toBytes( "4397465e-35f9-11eb-adc1-0242ac120002"); io:println("UUID bytes: ", uuidBytes1); // Converts a UUID string to a UUID record. uuid:Uuid uuidRecord1 = check uuid:toRecord( "4397465e-35f9-11eb-adc1-0242ac120002"); io:println("UUID record: ", uuidRecord1); uuid:Uuid uuidRecord = { timeLow: 1133987422, timeMid: 13817, timeHiAndVersion: 4587, clockSeqHiAndReserved: 173, clockSeqLo: 193, node: 2485377957890 }; // Converts a UUID record to a UUID string. string uuidString1 = check uuid:toString(uuidRecord); io:println("UUID string: ", uuidString1); // Converts a UUID record to an array of bytes. byte[] uuidBytes2 = check uuid:toBytes(uuidRecord); io:println("UUID bytes: ", uuidBytes2); // Converts a UUID bytes array to a UUID string. string uuidString2 = check uuid:toString( [67,151,70,94,53,249,17,235,173,193,2,66,172,18,0,2]); io:println("UUID string: ", uuidString2); // Converts a UUID bytes array to a UUID record. uuid:Uuid uuidRecord2 = check uuid:toRecord( [67,151,70,94,53,249,17,235,173,193,2,66,172,18,0,2]); io:println("UUID record: ", uuidRecord2); return; } ================================================ FILE: project-api-tests/src/test/resources/build-time/Project2/Ballerina.toml ================================================ [package] org = "testorg" name = "project1" version = "0.1.0" ================================================ FILE: project-api-tests/src/test/resources/build-time/Project2/main.bal ================================================ import ballerina/io; import ballerina/random; import ballerina/uuid; import ballerina/http as _; import ballerina/log as _; import ballerina/file as _; import ballerina/graphql as _; public function main() returns error? { // Generates a random decimal number between 0.0 and 1.0. float randomDecimal = random:createDecimal(); io:println("Random decimal number: ", randomDecimal); // Generates a random number between the given start(inclusive) and end(exclusive) values. int randomInteger = check random:createIntInRange(1, 100); io:println("Random integer number in range: ", randomInteger); return; } public function uuidGen() returns error? { // Tests a string to see if it is a valid UUID. boolean valid = uuid:validate("4397465e-35f9-11eb-adc1-0242ac120002"); io:println("UUID validated: ", valid.toString()); // Detects the RFC version of a UUID. uuid:Version v = check uuid:getVersion( "4397465e-35f9-11eb-adc1-0242ac120002"); io:println("UUID version: ", v.toString()); // Converts a UUID string to an array of bytes. byte[] uuidBytes1 = check uuid:toBytes( "4397465e-35f9-11eb-adc1-0242ac120002"); io:println("UUID bytes: ", uuidBytes1); // Converts a UUID string to a UUID record. uuid:Uuid uuidRecord1 = check uuid:toRecord( "4397465e-35f9-11eb-adc1-0242ac120002"); io:println("UUID record: ", uuidRecord1); uuid:Uuid uuidRecord = { timeLow: 1133987422, timeMid: 13817, timeHiAndVersion: 4587, clockSeqHiAndReserved: 173, clockSeqLo: 193, node: 2485377957890 }; // Converts a UUID record to a UUID string. string uuidString1 = check uuid:toString(uuidRecord); io:println("UUID string: ", uuidString1); // Converts a UUID record to an array of bytes. byte[] uuidBytes2 = check uuid:toBytes(uuidRecord); io:println("UUID bytes: ", uuidBytes2); // Converts a UUID bytes array to a UUID string. string uuidString2 = check uuid:toString( [67,151,70,94,53,249,17,235,173,193,2,66,172,18,0,2]); io:println("UUID string: ", uuidString2); // Converts a UUID bytes array to a UUID record. uuid:Uuid uuidRecord2 = check uuid:toRecord( [67,151,70,94,53,249,17,235,173,193,2,66,172,18,0,2]); io:println("UUID record: ", uuidRecord2); return; } ================================================ FILE: project-api-tests/src/test/resources/build-time/Project3/Ballerina.toml ================================================ [package] org = "testorg" name = "project1" version = "0.1.0" ================================================ FILE: project-api-tests/src/test/resources/build-time/Project3/main.bal ================================================ import ballerina/io; import ballerina/random; import ballerina/uuid; import ballerina/http as _; import ballerina/log as _; import ballerina/file as _; import ballerina/graphql as _; public function main() returns error? { // Generates a random decimal number between 0.0 and 1.0. float randomDecimal = random:createDecimal(); io:println("Random decimal number: ", randomDecimal); // Generates a random number between the given start(inclusive) and end(exclusive) values. int randomInteger = check random:createIntInRange(1, 100); io:println("Random integer number in range: ", randomInteger); return; } public function uuidGen() returns error? { // Tests a string to see if it is a valid UUID. boolean valid = uuid:validate("4397465e-35f9-11eb-adc1-0242ac120002"); io:println("UUID validated: ", valid.toString()); // Detects the RFC version of a UUID. uuid:Version v = check uuid:getVersion( "4397465e-35f9-11eb-adc1-0242ac120002"); io:println("UUID version: ", v.toString()); // Converts a UUID string to an array of bytes. byte[] uuidBytes1 = check uuid:toBytes( "4397465e-35f9-11eb-adc1-0242ac120002"); io:println("UUID bytes: ", uuidBytes1); // Converts a UUID string to a UUID record. uuid:Uuid uuidRecord1 = check uuid:toRecord( "4397465e-35f9-11eb-adc1-0242ac120002"); io:println("UUID record: ", uuidRecord1); uuid:Uuid uuidRecord = { timeLow: 1133987422, timeMid: 13817, timeHiAndVersion: 4587, clockSeqHiAndReserved: 173, clockSeqLo: 193, node: 2485377957890 }; // Converts a UUID record to a UUID string. string uuidString1 = check uuid:toString(uuidRecord); io:println("UUID string: ", uuidString1); // Converts a UUID record to an array of bytes. byte[] uuidBytes2 = check uuid:toBytes(uuidRecord); io:println("UUID bytes: ", uuidBytes2); // Converts a UUID bytes array to a UUID string. string uuidString2 = check uuid:toString( [67,151,70,94,53,249,17,235,173,193,2,66,172,18,0,2]); io:println("UUID string: ", uuidString2); // Converts a UUID bytes array to a UUID record. uuid:Uuid uuidRecord2 = check uuid:toRecord( [67,151,70,94,53,249,17,235,173,193,2,66,172,18,0,2]); io:println("UUID record: ", uuidRecord2); return; } ================================================ FILE: project-api-tests/src/test/resources/build-time/Project3/tests/test.bal ================================================ import ballerina/test; import ballerina/io; @test:Config {} function testFunction() { io:println("I'm in test function!"); test:assertEquals(10, 10); } ================================================ FILE: project-api-tests/src/test/resources/central/projectA/Ballerina.toml ================================================ [package] org = "bctestorg" name = "my_package" version = "1.0.0" readme = "Package.md" ================================================ FILE: project-api-tests/src/test/resources/central/projectA/Package.md ================================================ Prints "Hello World!" with a main function. [//]: # (above is the package summary) # Package Overview Prints "Hello World!" as the output to the command line using a main function. ================================================ FILE: project-api-tests/src/test/resources/central/projectA/main.bal ================================================ import ballerina/lang.'int as ints; public function main() { _ = testSum(); } public function testSum() returns int { return ints:sum(10, 25, 35, 40); } ================================================ FILE: project-api-tests/src/test/resources/central/projectB/Ballerina.toml ================================================ [package] org = "bctestorg" name = "my_package" version = "1.0.0" readme = "Package.md" [platform.java21] graalvmCompatible=true [[platform.java21.dependency]] groupId = "org.slf4j" artifactId = "slf4j-api" version = "1.7.30" ================================================ FILE: project-api-tests/src/test/resources/central/projectB/Package.md ================================================ Prints "Hello World!" with a main function. [//]: # (above is the package summary) # Package Overview Prints "Hello World!" as the output to the command line using a main function. ================================================ FILE: project-api-tests/src/test/resources/central/projectB/main.bal ================================================ public function main() { _ = hello("World"); } public function hello(string name) returns string { return "Hello " + name; } ================================================ FILE: project-api-tests/src/test/resources/central/projectC/Ballerina.toml ================================================ [package] org = "bctestorg" name = "my_package" version = "1.0.0" readme = "Package.md" ================================================ FILE: project-api-tests/src/test/resources/central/projectC/Package.md ================================================ Prints "Hello World!" with a main function. [//]: # (above is the package summary) # Package Overview Prints "Hello World!" as the output to the command line using a main function. ================================================ FILE: project-api-tests/src/test/resources/central/projectC/main.bal ================================================ import bctestorg/ as pkgA; import bctestorg/ as pkgB; public function main() { _ = pkgA:testSum(); _ = pkgB:hello("Package C"); } public function printHelloWithSum(string name) returns string { return pkgB:hello(name) + ":" + pkgA:testSum().toString(); } ================================================ FILE: project-api-tests/src/test/resources/central/projectD/Ballerina.toml ================================================ [package] org = "bctestorg" name = "my_package" version = "1.0.0" readme = "Package.md" ================================================ FILE: project-api-tests/src/test/resources/central/projectD/Package.md ================================================ Prints "Hello World!" with a main function. [//]: # (above is the package summary) # Package Overview Prints "Hello World!" as the output to the command line using a main function. ================================================ FILE: project-api-tests/src/test/resources/central/projectD/main.bal ================================================ import bctestorg/ as pkgC; public function main() { _ = pkgC:printHelloWithSum("World"); } ================================================ FILE: project-api-tests/src/test/resources/central/projectE/Ballerina.toml ================================================ [package] org = "bctestorg" name = "my_package" version = "1.0.0" readme = "Package.md" ================================================ FILE: project-api-tests/src/test/resources/central/projectE/Package.md ================================================ Prints "Hello World!" with a main function. [//]: # (above is the package summary) # Package Overview Prints "Hello World!" as the output to the command line using a main function. ================================================ FILE: project-api-tests/src/test/resources/central/projectE/main.bal ================================================ public function e() returns boolean { return true; } ================================================ FILE: project-api-tests/src/test/resources/central/projectF/Ballerina.toml ================================================ [package] org = "bctestorg" name = "my_package" version = "1.0.0" readme = "Package.md" ================================================ FILE: project-api-tests/src/test/resources/central/projectF/Package.md ================================================ Prints "Hello World!" with a main function. [//]: # (above is the package summary) # Package Overview Prints "Hello World!" as the output to the command line using a main function. ================================================ FILE: project-api-tests/src/test/resources/central/projectF/main.bal ================================================ import bctestorg/ as pkgE; public function f() returns boolean { return pkgE:e(); } ================================================ FILE: project-api-tests/src/test/resources/central/projectG/Ballerina.toml ================================================ [package] org = "bctestorg" name = "my_package" version = "1.0.0" readme = "Package.md" ================================================ FILE: project-api-tests/src/test/resources/central/projectG/Package.md ================================================ Prints "Hello World!" with a main function. [//]: # (above is the package summary) # Package Overview Prints "Hello World!" as the output to the command line using a main function. ================================================ FILE: project-api-tests/src/test/resources/central/projectG/main.bal ================================================ import bctestorg/ as pkgF; public function g() returns boolean { return pkgF:f(); } ================================================ FILE: project-api-tests/src/test/resources/central/projectSnapshot/Ballerina.toml ================================================ [package] org = "bctestorg" name = "my_package" version = "1.0.0-snapshot" readme = "Package.md" ================================================ FILE: project-api-tests/src/test/resources/central/projectSnapshot/Package.md ================================================ Prints "Hello World!" with a main function. [//]: # (above is the package summary) # Package Overview Prints "Hello World!" as the output to the command line using a main function. ================================================ FILE: project-api-tests/src/test/resources/central/projectSnapshot/main.bal ================================================ public function main() { } ================================================ FILE: project-api-tests/src/test/resources/distribution-tests/disttestmultiples/Ballerina.toml ================================================ [package] org = "bctestorg" name = "disttestmultiples" version = "1.1.1" readme = "Package.md" ================================================ FILE: project-api-tests/src/test/resources/distribution-tests/disttestmultiples/Module.md ================================================ Prints "Hello, World!" with a main function. [//]: # (above is the module summary) # Module Overview Provides an overview about the module when generating the API documentations. For example, refer to https://lib.ballerina.io/ballerina/io/latest ================================================ FILE: project-api-tests/src/test/resources/distribution-tests/disttestmultiples/Package.md ================================================ Prints "Hello, World!" with a hello function. [//]: # (above is the package summary) # Package Overview Prints "Hello, World!" as the output to the command line using a hello function. ================================================ FILE: project-api-tests/src/test/resources/distribution-tests/disttestmultiples/disttestmultiples.bal ================================================ # Prints `Hello` with input string name. # # + name - the input sting name # + return - "Hello, " with the input string name public function hello(string name) returns string { if !(name is "") { return "Hello, " + name; } return "Hello, World!"; } ================================================ FILE: project-api-tests/src/test/resources/distribution-tests/disttestpack1/Ballerina.toml ================================================ [package] org = "bctestorg" name = "disttestpack1" version = "1.1.0" readme = "Package.md" ================================================ FILE: project-api-tests/src/test/resources/distribution-tests/disttestpack1/Module.md ================================================ Prints "Hello, World!" with a main function. [//]: # (above is the module summary) # Module Overview Provides an overview about the module when generating the API documentations. For example, refer to https://lib.ballerina.io/ballerina/io/latest ================================================ FILE: project-api-tests/src/test/resources/distribution-tests/disttestpack1/Package.md ================================================ Prints "Hello, World!" with a hello function. [//]: # (above is the package summary) # Package Overview Prints "Hello, World!" as the output to the command line using a hello function. ================================================ FILE: project-api-tests/src/test/resources/distribution-tests/disttestpack1/disttestpack1.bal ================================================ # Prints `Hello` with input string name. # # + name - the input sting name # + return - "Hello, " with the input string name public function hello(string name) returns string { if !(name is "") { return "Hello, " + name; } return "Hello, World!"; } ================================================ FILE: project-api-tests/src/test/resources/distribution-tests/disttestpack2/Ballerina.toml ================================================ [package] org = "bctestorg" name = "disttestpack2" version = "0.1.0" ================================================ FILE: project-api-tests/src/test/resources/distribution-tests/disttestpack2/main.bal ================================================ import bctestorg/disttestpack1 as _; // Prints `Hello, World!`. public function main() { } ================================================ FILE: project-api-tests/src/test/resources/distribution-tests/disttestpack3/Ballerina.toml ================================================ [package] org = "bctestorg" name = "disttestpack3" version = "0.1.0" ================================================ FILE: project-api-tests/src/test/resources/distribution-tests/disttestpack3/main.bal ================================================ import bctestorg/disttestpackbeta6 as _; public function main() { } ================================================ FILE: project-api-tests/src/test/resources/distribution-tests/disttestpack4/Ballerina.toml ================================================ [package] org = "bctestorg" name = "disttestpack4" version = "0.1.0" ================================================ FILE: project-api-tests/src/test/resources/distribution-tests/disttestpack4/main.bal ================================================ import bctestorg/forwardpack1 as _; // Prints `Hello, World!`. public function main() { } ================================================ FILE: project-api-tests/src/test/resources/distribution-tests/disttestpack5/Ballerina.toml ================================================ [package] org = "bctestorg" name = "disttestpack5" version = "0.1.0" ================================================ FILE: project-api-tests/src/test/resources/distribution-tests/disttestpack5/main.bal ================================================ import bctestorg/disttestmultiples as _; // Prints `Hello, World!`. public function main() { } ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageH.test/.gitignore ================================================ target ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageH.test/Ballerina.toml ================================================ [package] org = "bctestorg" name = "PackageH.test" version = "1.0.0" readme = "Package.md" [[package.modules]] name = "PackageH.test.mod.api" export = true ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageH.test/Module.md ================================================ Prints "Hello World!" with a main function. [//]: # (above is the module summary) # Module Overview Provides an overview about the module when generating the API documentations. For example, refer to https://lib.ballerina.io/ballerina/io/0.6.0-beta.2 ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageH.test/Package.md ================================================ Prints "Hello World!" with a hello function. [//]: # (above is the package summary) # Package Overview Prints "Hello World!" as the output to the command line using a hello function. ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageH.test/lib.bal ================================================ # Prints `Hello` with input string name. # # + name - the input sting name # + return - "Hello, " with the input string name public function hello(string name) returns string { if !(name is "") { return "Hello, " + name; } return "Hello, World!"; } ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageH.test/modules/mod.api/Module.md ================================================ Prints "Hello World!" with a main function. [//]: # (above is the module summary) # Module Overview Provides an overview about the module when generating the API documentations. For example, refer to https://lib.ballerina.io/ballerina/io/0.6.0-beta.2 ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageH.test/modules/mod.api/Package.md ================================================ Prints "Hello World!" with a hello function. [//]: # (above is the package summary) # Package Overview Prints "Hello World!" as the output to the command line using a hello function. ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageH.test/modules/mod.api/mod.api.bal ================================================ # Prints `Hello` with input string name. # # + name - the input sting name # + return - "Hello, " with the input string name public function hello(string name) returns string { if !(name is "") { return "Hello, " + name; } return "Hello, World!"; } ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageH.test.mod/.gitignore ================================================ target ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageH.test.mod/Ballerina.toml ================================================ [package] org = "bctestorg" name = "PackageH.test.mod" version = "1.0.1" readme = "Package.md" [[package.modules]] name = "PackageH.test.mod.api" export = true [[package.modules]] name = "PackageH.test.mod.api.doc" export = true ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageH.test.mod/Module.md ================================================ Prints "Hello World!" with a main function. [//]: # (above is the module summary) # Module Overview Provides an overview about the module when generating the API documentations. For example, refer to https://lib.ballerina.io/ballerina/io/0.6.0-beta.2 ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageH.test.mod/Package.md ================================================ Prints "Hello World!" with a hello function. [//]: # (above is the package summary) # Package Overview Prints "Hello World!" as the output to the command line using a hello function. ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageH.test.mod/lib.bal ================================================ # Prints `Hello` with input string name. # # + name - the input sting name # + return - "Hello, " with the input string name public function hello(string name) returns string { if !(name is "") { return "Hello, " + name; } return "Hello, World!"; } ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageH.test.mod/modules/api/Module.md ================================================ Prints "Hello World!" with a main function. [//]: # (above is the module summary) # Module Overview Provides an overview about the module when generating the API documentations. For example, refer to https://lib.ballerina.io/ballerina/io/0.6.0-beta.2 ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageH.test.mod/modules/api/Package.md ================================================ Prints "Hello World!" with a hello function. [//]: # (above is the package summary) # Package Overview Prints "Hello World!" as the output to the command line using a hello function. ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageH.test.mod/modules/api/api.bal ================================================ # Prints `Hello` with input string name. # # + name - the input sting name # + return - "Hello, " with the input string name public function hello(string name) returns string { if !(name is "") { return "Hello, " + name; } return "Hello, World!"; } ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageH.test.mod/modules/api.doc/Module.md ================================================ Prints "Hello World!" with a main function. [//]: # (above is the module summary) # Module Overview Provides an overview about the module when generating the API documentations. For example, refer to https://lib.ballerina.io/ballerina/io/0.6.0-beta.2 ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageH.test.mod/modules/api.doc/Package.md ================================================ Prints "Hello World!" with a hello function. [//]: # (above is the package summary) # Package Overview Prints "Hello World!" as the output to the command line using a hello function. ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageH.test.mod/modules/api.doc/api.doc.bal ================================================ # Prints `Hello` with input string name. # # + name - the input sting name # + return - "Hello, " with the input string name public function hello(string name) returns string { if !(name is "") { return "Hello, " + name; } return "Hello, World!"; } ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageI/.gitignore ================================================ target ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageI/Ballerina.toml ================================================ [build-options] observabilityIncluded = false ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageI/Dependencies-template.toml ================================================ # AUTO-GENERATED FILE. DO NOT MODIFY. # This file is auto-generated by Ballerina for managing dependency versions. # It should not be modified by hand. [ballerina] dependencies-toml-version = "2" distribution-version = "**INSERT_DISTRIBUTION_VERSION_HERE**" [[package]] org = "bctestorg" name = "PackageH.test" version = "1.0.0" modules = [ {org = "bctestorg", packageName = "PackageH.test", moduleName = "PackageH.test"}, {org = "bctestorg", packageName = "PackageH.test", moduleName = "PackageH.test.mod.api"} ] [[package]] org = "bctestorg" name = "PackageI" version = "0.1.0" dependencies = [ {org = "bctestorg", name = "PackageH.test"} ] modules = [ {org = "bctestorg", packageName = "PackageI", moduleName = "PackageI"} ] ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageI/main.bal ================================================ import bctestorg/PackageH.test.mod.api as _; public function main() { } ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageJ.test/Ballerina.toml ================================================ [package] org = "bctestorg" name = "PackageJ.test" version = "1.0.0" readme = "Package.md" ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageJ.test/Module.md ================================================ Prints "Hello World!" with a main function. [//]: # (above is the module summary) # Module Overview Provides an overview about the module when generating the API documentations. For example, refer to https://lib.ballerina.io/ballerina/io/0.6.0-beta.2 ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageJ.test/Package.md ================================================ Prints "Hello World!" with a hello function. [//]: # (above is the package summary) # Package Overview Prints "Hello World!" as the output to the command line using a hello function. ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageJ.test/lib.bal ================================================ # Prints `Hello` with input string name. # # + name - the input sting name # + return - "Hello, " with the input string name public function hello(string name) returns string { if !(name is "") { return "Hello, " + name; } return "Hello, World!"; } ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageK/.gitignore ================================================ target ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageK/Ballerina.toml ================================================ [package] org = "bctestorg" name = "PackageK" version = "0.1.0" [build-options] observabilityIncluded = false ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageK/Dependencies-template.toml ================================================ # AUTO-GENERATED FILE. DO NOT MODIFY. # This file is auto-generated by Ballerina for managing dependency versions. # It should not be modified by hand. [ballerina] dependencies-toml-version = "2" distribution-version = "**INSERT_DISTRIBUTION_VERSION_HERE**" [[package]] org = "bctestorg" name = "PackageJ.test" version = "1.0.0" modules = [ {org = "bctestorg", packageName = "PackageJ.test", moduleName = "PackageJ.test"} ] [[package]] org = "bctestorg" name = "PackageK" version = "0.1.0" dependencies = [ {org = "bctestorg", name = "PackageJ.test"} ] modules = [ {org = "bctestorg", packageName = "PackageK", moduleName = "PackageK"} ] ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageK/main.bal ================================================ import bctestorg/PackageJ.test as _; // Prints `Hello World`. public function main() { } ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageL.test/.gitignore ================================================ target ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageL.test/Ballerina.toml ================================================ [package] org = "bctestorg" name = "PackageL.test" version = "1.0.0" readme = "Package.md" [[package.modules]] name = "PackageL.test.mod.api" export = true ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageL.test/Dependencies-template.toml ================================================ # AUTO-GENERATED FILE. DO NOT MODIFY. # This file is auto-generated by Ballerina for managing dependency versions. # It should not be modified by hand. [ballerina] dependencies-toml-version = "2" distribution-version = "**INSERT_DISTRIBUTION_VERSION_HERE**" [[package]] org = "bctestorg" name = "PackageL.test" version = "1.0.0" modules = [ {org = "bctestorg", packageName = "PackageL.test", moduleName = "PackageL.test"}, {org = "bctestorg", packageName = "PackageL.test", moduleName = "PackageL.test.mod.api"} ] ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageL.test/Module.md ================================================ Prints "Hello World!" with a main function. [//]: # (above is the module summary) # Module Overview Provides an overview about the module when generating the API documentations. For example, refer to https://lib.ballerina.io/ballerina/io/0.6.0-beta.2 ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageL.test/Package.md ================================================ Prints "Hello World!" with a hello function. [//]: # (above is the package summary) # Package Overview Prints "Hello World!" as the output to the command line using a hello function. ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageL.test/lib.bal ================================================ # Prints `Hello` with input string name. # # + name - the input sting name # + return - "Hello, " with the input string name public function hello(string name) returns string { if !(name is "") { return "Hello, " + name; } return "Hello, World!"; } ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageL.test/modules/Module.md ================================================ Prints "Hello World!" with a main function. [//]: # (above is the module summary) # Module Overview Provides an overview about the module when generating the API documentations. For example, refer to https://lib.ballerina.io/ballerina/io/0.6.0-beta.2 ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageL.test/modules/Package.md ================================================ Prints "Hello World!" with a hello function. [//]: # (above is the package summary) # Package Overview Prints "Hello World!" as the output to the command line using a hello function. ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageL.test/modules/mod.api/Module.md ================================================ Prints "Hello World!" with a main function. [//]: # (above is the module summary) # Module Overview Provides an overview about the module when generating the API documentations. For example, refer to https://lib.ballerina.io/ballerina/io/0.6.0-beta.2 ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageL.test/modules/mod.api/Package.md ================================================ Prints "Hello World!" with a hello function. [//]: # (above is the package summary) # Package Overview Prints "Hello World!" as the output to the command line using a hello function. ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageL.test/modules/mod.api/mod.api.bal ================================================ # Prints `Hello` with input string name. # # + name - the input sting name # + return - "Hello, " with the input string name public function hello(string name) returns string { if !(name is "") { return "Hello, " + name; } return "Hello, World!"; } ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageM/.gitignore ================================================ target ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageM/Ballerina.toml ================================================ [package] org = "bctestorg" name = "PackageM" version = "0.1.0" [build-options] observabilityIncluded = false ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageM/Dependencies-template.toml ================================================ # AUTO-GENERATED FILE. DO NOT MODIFY. # This file is auto-generated by Ballerina for managing dependency versions. # It should not be modified by hand. [ballerina] dependencies-toml-version = "2" distribution-version = "**INSERT_DISTRIBUTION_VERSION_HERE**" [[package]] org = "bctestorg" name = "PackageL.test" version = "1.0.0" modules = [ {org = "bctestorg", packageName = "PackageL.test", moduleName = "PackageL.test"}, {org = "bctestorg", packageName = "PackageL.test", moduleName = "PackageL.test.mod.api"} ] [[package]] org = "bctestorg" name = "PackageM" version = "0.1.0" dependencies = [ {org = "bctestorg", name = "PackageL.test"} ] modules = [ {org = "bctestorg", packageName = "PackageM", moduleName = "PackageM"} ] ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageM/main.bal ================================================ import bctestorg/PackageL.test.mod.api as _; // Prints `Hello World`. public function main() { } ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageN/.gitignore ================================================ target ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageN/Ballerina.toml ================================================ [package] org = "bctestorg" name = "PackageN" version = "0.1.0" [build-options] observabilityIncluded = false ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageN/Dependencies-template.toml ================================================ # AUTO-GENERATED FILE. DO NOT MODIFY. # This file is auto-generated by Ballerina for managing dependency versions. # It should not be modified by hand. [ballerina] dependencies-toml-version = "2" distribution-version = "**INSERT_DISTRIBUTION_VERSION_HERE**" [[package]] org = "bctestorg" name = "PackageH.test" version = "1.0.0" modules = [ {org = "bctestorg", packageName = "PackageH.test", moduleName = "PackageH.test"}, {org = "bctestorg", packageName = "PackageH.test", moduleName = "PackageH.test.mod.api"} ] [[package]] org = "bctestorg" name = "PackageN" version = "0.1.0" dependencies = [ {org = "bctestorg", name = "PackageH.test"} ] modules = [ {org = "bctestorg", packageName = "PackageN", moduleName = "PackageN"} ] ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageN/main.bal ================================================ import bctestorg/PackageH.test.mod.api as _; // Prints `Hello World`. public function main() { } ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageO.test/.gitignore ================================================ target ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageO.test/Ballerina.toml ================================================ [package] org = "bctestorg" name = "PackageO.test" version = "1.0.0" readme = "Package.md" ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageO.test/Module.md ================================================ Prints "Hello World!" with a main function. [//]: # (above is the module summary) # Module Overview Provides an overview about the module when generating the API documentations. For example, refer to https://lib.ballerina.io/ballerina/io/0.6.0-beta.2 ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageO.test/Package.md ================================================ Prints "Hello World!" with a hello function. [//]: # (above is the package summary) # Package Overview Prints "Hello World!" as the output to the command line using a hello function. ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageO.test/lib.bal ================================================ # Prints `Hello` with input string name. # # + name - the input sting name # + return - "Hello, " with the input string name public function hello(string name) returns string { if !(name is "") { return "Hello, " + name; } return "Hello, World!"; } ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageP/.gitignore ================================================ target ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageP/Ballerina.toml ================================================ [package] org = "bctestorg" name = "PackageP" version = "0.1.0" [build-options] observabilityIncluded = false ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageP/main.bal ================================================ public function main() { } ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageQ.test/.gitignore ================================================ target ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageQ.test/Ballerina.toml ================================================ [package] org = "bctestorg" name = "PackageQ.test" version = "1.0.0" readme = "Package.md" [[package.modules]] name = "PackageQ.test.mod.api" export = true ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageQ.test/Dependencies-template.toml ================================================ # AUTO-GENERATED FILE. DO NOT MODIFY. # This file is auto-generated by Ballerina for managing dependency versions. # It should not be modified by hand. [ballerina] dependencies-toml-version = "2" distribution-version = "**INSERT_DISTRIBUTION_VERSION_HERE**" [[package]] org = "bctestorg" name = "PackageQ.test" version = "1.0.0" modules = [ {org = "bctestorg", packageName = "PackageQ.test", moduleName = "PackageQ.test"}, {org = "bctestorg", packageName = "PackageQ.test", moduleName = "PackageQ.test.mod.api"} ] ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageQ.test/Module.md ================================================ Prints "Hello World!" with a main function. [//]: # (above is the module summary) # Module Overview Provides an overview about the module when generating the API documentations. For example, refer to https://lib.ballerina.io/ballerina/io/0.6.0-beta.2 ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageQ.test/Package.md ================================================ Prints "Hello World!" with a hello function. [//]: # (above is the package summary) # Package Overview Prints "Hello World!" as the output to the command line using a hello function. ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageQ.test/lib.bal ================================================ # Prints `Hello` with input string name. # # + name - the input sting name # + return - "Hello, " with the input string name public function hello(string name) returns string { if !(name is "") { return "Hello, " + name; } return "Hello, World!"; } ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageQ.test/modules/Module.md ================================================ Prints "Hello World!" with a main function. [//]: # (above is the module summary) # Module Overview Provides an overview about the module when generating the API documentations. For example, refer to https://lib.ballerina.io/ballerina/io/0.6.0-beta.2 ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageQ.test/modules/Package.md ================================================ Prints "Hello World!" with a hello function. [//]: # (above is the package summary) # Package Overview Prints "Hello World!" as the output to the command line using a hello function. ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageQ.test/modules/mod.api/Module.md ================================================ Prints "Hello World!" with a main function. [//]: # (above is the module summary) # Module Overview Provides an overview about the module when generating the API documentations. For example, refer to https://lib.ballerina.io/ballerina/io/0.6.0-beta.2 ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageQ.test/modules/mod.api/Package.md ================================================ Prints "Hello World!" with a hello function. [//]: # (above is the package summary) # Package Overview Prints "Hello World!" as the output to the command line using a hello function. ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageQ.test/modules/mod.api/mod.api.bal ================================================ # Prints `Hello` with input string name. # # + name - the input sting name # + return - "Hello, " with the input string name public function hello(string name) returns string { if !(name is "") { return "Hello, " + name; } return "Hello, World!"; } ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageR/.gitignore ================================================ target ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageR/Ballerina.toml ================================================ [package] org = "bctestorg" name = "PackageR" version = "0.1.0" [build-options] observabilityIncluded = false [[dependency]] org = "bctestorg" name = "PackageQ.test" version = "1.0.0" repository = "local" ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageR/main.bal ================================================ import bctestorg/PackageQ.test.mod.api as _; public function main() { } ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageR.test/.gitignore ================================================ target ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageR.test/Ballerina.toml ================================================ [package] org = "bctestorg" name = "PackageR.test" version = "1.0.0" readme = "Package.md" ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageR.test/Dependencies-template.toml ================================================ # AUTO-GENERATED FILE. DO NOT MODIFY. # This file is auto-generated by Ballerina for managing dependency versions. # It should not be modified by hand. [ballerina] dependencies-toml-version = "2" distribution-version = "**INSERT_DISTRIBUTION_VERSION_HERE**" [[package]] org = "bctestorg" name = "PackageH.test" version = "1.0.0" modules = [ {org = "bctestorg", packageName = "PackageH.test", moduleName = "PackageH.test"}, {org = "bctestorg", packageName = "PackageH.test", moduleName = "PackageH.test.mod.api"} ] [[package]] org = "bctestorg" name = "PackageR.test" version = "1.0.0" dependencies = [ {org = "bctestorg", name = "PackageH.test"} ] modules = [ {org = "bctestorg", packageName = "PackageR.test", moduleName = "PackageR.test"} ] ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageR.test/Module.md ================================================ Prints "Hello World!" with a main function. [//]: # (above is the module summary) # Module Overview Provides an overview about the module when generating the API documentations. For example, refer to https://lib.ballerina.io/ballerina/io/0.6.0-beta.2 ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageR.test/Package.md ================================================ Prints "Hello World!" with a hello function. [//]: # (above is the package summary) # Package Overview Prints "Hello World!" as the output to the command line using a hello function. ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageR.test/lib.bal ================================================ import bctestorg/PackageH.test.mod.api as _; # Prints `Hello` with input string name. # # + name - the input sting name # + return - "Hello, " with the input string name public function hello(string name) returns string { if !(name is "") { return "Hello, " + name; } return "Hello, World!"; } ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageS/.gitignore ================================================ target ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageS/Ballerina.toml ================================================ [package] org = "bctestorg" name = "PackageS" version = "0.1.0" [build-options] observabilityIncluded = false ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageS/Dependencies-template.toml ================================================ # AUTO-GENERATED FILE. DO NOT MODIFY. # This file is auto-generated by Ballerina for managing dependency versions. # It should not be modified by hand. [ballerina] dependencies-toml-version = "2" distribution-version = "**INSERT_DISTRIBUTION_VERSION_HERE**" [[package]] org = "bctestorg" name = "PackageH.test" version = "1.0.0" [[package]] org = "bctestorg" name = "PackageR.test" version = "1.0.0" dependencies = [ {org = "bctestorg", name = "PackageH.test"} ] modules = [ {org = "bctestorg", packageName = "PackageR.test", moduleName = "PackageR.test"} ] [[package]] org = "bctestorg" name = "PackageS" version = "0.1.0" dependencies = [ {org = "bctestorg", name = "PackageR.test"} ] modules = [ {org = "bctestorg", packageName = "PackageS", moduleName = "PackageS"} ] ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageS/main.bal ================================================ import bctestorg/PackageR.test as _; public function main() { } ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageT.test/.gitignore ================================================ target ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageT.test/Ballerina.toml ================================================ [package] org = "bctestorg" name = "PackageT.test" version = "1.0.0-beta.1" readme = "Package.md" ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageT.test/Module.md ================================================ Prints "Hello World!" with a main function. [//]: # (above is the module summary) # Module Overview Provides an overview about the module when generating the API documentations. For example, refer to https://lib.ballerina.io/ballerina/io/0.6.0-beta.2 ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageT.test/Package.md ================================================ Prints "Hello World!" with a hello function. [//]: # (above is the package summary) # Package Overview Prints "Hello World!" as the output to the command line using a hello function. ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageT.test/lib.bal ================================================ # Prints `Hello` with input string name. # # + name - the input sting name # + return - "Hello, " with the input string name public function hello(string name) returns string { if !(name is "") { return "Hello, " + name; } return "Hello, World!"; } ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageU/.gitignore ================================================ target ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageU/Ballerina.toml ================================================ [build-options] observabilityIncluded = false ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageU/Dependencies-template.toml ================================================ # AUTO-GENERATED FILE. DO NOT MODIFY. # This file is auto-generated by Ballerina for managing dependency versions. # It should not be modified by hand. [ballerina] dependencies-toml-version = "2" distribution-version = "**INSERT_DISTRIBUTION_VERSION_HERE**" [[package]] org = "bctestorg" name = "PackageT.test" version = "1.0.0-beta.1" modules = [ {org = "bctestorg", packageName = "PackageT.test", moduleName = "PackageT.test"} ] [[package]] org = "bctestorg" name = "PackageU" version = "0.1.0" dependencies = [ {org = "bctestorg", name = "PackageT.test"} ] modules = [ {org = "bctestorg", packageName = "PackageU", moduleName = "PackageU"} ] ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/PackageU/main.bal ================================================ import bctestorg/PackageT.test as _; public function main() { } ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/Transitive.PackageH.test/.gitignore ================================================ target ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/Transitive.PackageH.test/Ballerina.toml ================================================ [package] org = "bctestorg" name = "Transitive.PackageH.test" version = "1.0.0" readme = "Package.md" ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/Transitive.PackageH.test/Module.md ================================================ Prints "Hello World!" with a main function. [//]: # (above is the module summary) # Module Overview Provides an overview about the module when generating the API documentations. For example, refer to https://lib.ballerina.io/ballerina/io/0.6.0-beta.2 ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/Transitive.PackageH.test/Package.md ================================================ Prints "Hello World!" with a hello function. [//]: # (above is the package summary) # Package Overview Prints "Hello World!" as the output to the command line using a hello function. ================================================ FILE: project-api-tests/src/test/resources/hierarchical-packages/Transitive.PackageH.test/lib.bal ================================================ # Prints `Hello` with input string name. # # + name - the input sting name # + return - "Hello, " with the input string name public function hello(string name) returns string { if !(name is "") { return "Hello, " + name; } return "Hello, World!"; } ================================================ FILE: project-api-tests/src/test/resources/maven-repos/myproject1/.devcontainer.json ================================================ { "image": "ballerina/ballerina-devcontainer:2201.12.10", "customizations": { "vscode": { "extensions": ["WSO2.ballerina"] } } } ================================================ FILE: project-api-tests/src/test/resources/maven-repos/myproject1/.gitignore ================================================ # Ballerina generates this directory during the compilation of a package. # It contains compiler-generated artifacts and the final executable if this is an application package. target/ # Ballerina maintains the compiler-generated source code here. # Remove this if you want to commit generated sources. generated/ # Contains configuration values used during development time. # See https://ballerina.io/learn/provide-values-to-configurable-variables/ for more details. Config.toml ================================================ FILE: project-api-tests/src/test/resources/maven-repos/myproject1/Ballerina.toml ================================================ [package] org = "bctestorg" name = "myproject1" version = "0.1.0" distribution = "2201.12.10" readme = "Package.md" [build-options] observabilityIncluded = true [[dependency]] org="bctestorg" name="pkg1" version="0.1.0" repository="github1" [[dependency]] org="bctestorg" name="pkg2" version="1.0.0" repository="github1" ================================================ FILE: project-api-tests/src/test/resources/maven-repos/myproject1/Package.md ================================================ Package documentation for myproject1. ================================================ FILE: project-api-tests/src/test/resources/maven-repos/myproject1/main.bal ================================================ import bctestorg/pkg1; import bctestorg/pkg2; public function main() { pkg1:main(); pkg2:main(); } ================================================ FILE: project-api-tests/src/test/resources/maven-repos/pact/.devcontainer.json ================================================ { "image": "ballerina/ballerina-devcontainer:2201.6.0", "extensions": ["WSO2.ballerina"] } ================================================ FILE: project-api-tests/src/test/resources/maven-repos/pact/.gitignore ================================================ generated Config.toml ================================================ FILE: project-api-tests/src/test/resources/maven-repos/pact/Ballerina.toml ================================================ [package] org = "bctestorg" name = "pact" version = "0.1.0" readme = "Package.md" [build-options] observabilityIncluded = true ================================================ FILE: project-api-tests/src/test/resources/maven-repos/pact/Package.md ================================================ jsjj ================================================ FILE: project-api-tests/src/test/resources/maven-repos/pact/main.bal ================================================ public function main() { } ================================================ FILE: project-api-tests/src/test/resources/maven-repos/pkg1/.devcontainer.json ================================================ { "image": "ballerina/ballerina-devcontainer:2201.6.0", "extensions": ["WSO2.ballerina"] } ================================================ FILE: project-api-tests/src/test/resources/maven-repos/pkg1/.gitignore ================================================ target generated Config.toml ================================================ FILE: project-api-tests/src/test/resources/maven-repos/pkg1/Ballerina.toml ================================================ [package] org = "bctestorg" name = "pkg1" version = "0.1.0" readme = "Package.md" [build-options] observabilityIncluded = true ================================================ FILE: project-api-tests/src/test/resources/maven-repos/pkg1/Package.md ================================================ Package documentation for pkg1. ================================================ FILE: project-api-tests/src/test/resources/maven-repos/pkg1/main.bal ================================================ public function main() { } ================================================ FILE: project-api-tests/src/test/resources/maven-repos/pkg2/.devcontainer.json ================================================ { "image": "ballerina/ballerina-devcontainer:2201.6.0", "extensions": ["WSO2.ballerina"] } ================================================ FILE: project-api-tests/src/test/resources/maven-repos/pkg2/.gitignore ================================================ target generated Config.toml ================================================ FILE: project-api-tests/src/test/resources/maven-repos/pkg2/Ballerina.toml ================================================ [package] org = "bctestorg" name = "pkg2" version = "1.0.0" readme = "Package.md" [build-options] observabilityIncluded = true ================================================ FILE: project-api-tests/src/test/resources/maven-repos/pkg2/Package.md ================================================ Package documentation for pkg2. ================================================ FILE: project-api-tests/src/test/resources/maven-repos/pkg2/main.bal ================================================ public function main() { } ================================================ FILE: project-api-tests/src/test/resources/maven-repos/pkg3/.devcontainer.json ================================================ { "image": "ballerina/ballerina-devcontainer:2201.6.0", "extensions": ["WSO2.ballerina"] } ================================================ FILE: project-api-tests/src/test/resources/maven-repos/pkg3/.gitignore ================================================ target generated Config.toml ================================================ FILE: project-api-tests/src/test/resources/maven-repos/pkg3/Ballerina.toml ================================================ [package] org = "bctestorg" name = "pkg3" version = "1.0.0" readme = "Package.md" [[dependency]] org = "bctestorg" name = "pkg2" version = "1.0.0" [build-options] observabilityIncluded = true ================================================ FILE: project-api-tests/src/test/resources/maven-repos/pkg3/Package.md ================================================ Package documentation for pkg3. ================================================ FILE: project-api-tests/src/test/resources/maven-repos/pkg3/main.bal ================================================ public function main() { } ================================================ FILE: project-api-tests/src/test/resources/maven-repos/test-resolution/.devcontainer.json ================================================ { "image": "ballerina/ballerina-devcontainer:2201.6.0", "extensions": ["WSO2.ballerina"], } ================================================ FILE: project-api-tests/src/test/resources/maven-repos/test-resolution/.gitignore ================================================ target generated Config.toml ================================================ FILE: project-api-tests/src/test/resources/maven-repos/test-resolution/Ballerina.toml ================================================ [package] org = "bctestorg" name = "test" version = "0.1.0" [build-options] observabilityIncluded = true [[dependency]] org = "bctestorg" name = "pact" version = "0.2.0" repository="github1" ================================================ FILE: project-api-tests/src/test/resources/maven-repos/test-resolution/main.bal ================================================ import bctestorg/pact as _; import ballerina/io; public function main() { io:println("Hello World!"); } ================================================ FILE: project-api-tests/src/test/resources/testng.xml ================================================ ================================================ FILE: pull_request_template.md ================================================ ## Purpose > Describe the problems, issues, or needs driving this feature/fix and include links to related issues in the following format: Resolves issue1, issue2, etc. ## Goals > Describe the solutions that this feature/fix will introduce to resolve the problems described above ## Approach > Describe how you are implementing the solutions. Include an animated GIF or screenshot if the change affects the UI (email documentation@wso2.com to review all UI text). Include a link to a Markdown file or Google doc if the feature write-up is too long to paste here. ## User stories > Summary of user stories addressed by this change> ## Release note > Brief description of the new feature or bug fix as it will appear in the release notes ## Documentation > Link(s) to product documentation that addresses the changes of this PR. If no doc impact, enter “N/A” plus brief explanation of why there’s no doc impact ## Training > Link to the PR for changes to the training content in https://github.com/wso2/WSO2-Training, if applicable ## Certification > Type “Sent” when you have provided new/updated certification questions, plus four answers for each question (correct answer highlighted in bold), based on this change. Certification questions/answers should be sent to certification@wso2.com and NOT pasted in this PR. If there is no impact on certification exams, type “N/A” and explain why. ## Marketing > Link to drafts of marketing content that will describe and promote this feature, including product page changes, technical articles, blog posts, videos, etc., if applicable ## Automation tests - Unit tests > Code coverage information - Integration tests > Details about the test cases and coverage ## Security checks - Followed secure coding standards in http://wso2.com/technical-reports/wso2-secure-engineering-guidelines? yes/no - Ran FindSecurityBugs plugin and verified report? yes/no - Confirmed that this PR doesn't commit any keys, passwords, tokens, usernames, or other secrets? yes/no ## Samples > Provide high-level details about the samples related to this feature ## Related PRs > List any other related PRs ## Migrations (if applicable) > Describe migration steps and platforms on which migration has been tested ## Test environment > List all JDK versions, operating systems, databases, and browser/versions on which this feature/fix was tested ## Learning > Describe the research phase and any blog posts, patterns, libraries, or add-ons you used to solve the problem. ================================================ FILE: release-notes.md ================================================ # Overview to Ballerina 0.970.0 Brief summary of overview and primary improvements of the new release # Compatibility and Support Any changes that break backwards compatibility such as syntax changes. Porting concepts, removal/addition of OS support. We can include any known issues for a OS or any platform # How to Update? Explain how to update this specific version. Specify about backward compatibility with previous components versions # Improvements ## Language & Runtime - improvement1 - improvement2 ## Standard Library - improvement1 - improvement2 ## Build & Package Management ## IDEs & Language Server ## Ballerina for Java ## Ballerina Sidecar ## Ballerina API Gateway ## Ballerina Message Broker ## Ballerina Transaction Coordinator ## Ballerina Observability # Bug Fixes Link to the Github issues filtering for all the components # Getting Started You can download the Ballerina distributions, try samples, and read the documentation at http://ballerinalang.org. You can also visit [Quick Tour][1] to get started. We encourage you to report issues, improvements, and suggestions at [Ballerina Github Repository][2]. [1]: https://ballerinalang.org/docs/quick-tour/quick-tour [2]: https://github.com/ballerina-lang/ballerina/ ================================================ FILE: resources/tools/COPYRIGHT.txt ================================================ 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: resources/tools/INSTALL.txt ================================================ ~ BALLERINA ~ ============= PREREQUISITES ------------- Ballerina requires JRE 17. Check https://adoptopenjdk.net/releases.html?variant=openjdk21&jvmVariant=hotspot to download the JRE relevant to your system. This is the Java version we have used when testing Ballerina. INSTALLING BALLERINA -------------------- - Unzip the Ballerina archive in the desired directory. This will be the Ballerina installation directory. - Set the path of the installation directory to the BALLERINA_HOME environment variable. - Add the `/bin` path to your system's path variable. - If you have installed Java 21 already, you are all set! Just make sure that the JAVA_HOME environment variable is set correctly. - If you do not have Java 21 or if you have a different version of Java, download a compatible JRE 17 version as specified in the prerequisites. - To set the obtained JRE as the one used by Ballerina, extract the downloaded JRE `zip/tar.gz` to the `/dependencies/jdk-21.0.5+11-jre` directory. - Once you are done, the directory structure should look exactly like the following: /dependencies/ └── jdk-21.0.5+11-jre ├── bin ├── bundle ├── conf ├── legal ├── lib ├── man └── release - Ensure that the directory which contains the JRE is named "jdk-21.0.5+11-jre" - Finally, to verify whether Ballerina is installed properly, you can run the `bal version` command. If Ballerina was installed correctly, you should see an output similar to the following: $ bal version Ballerina Swan Lake Alpha 1 Language specification v2020-12-17 Update Tool 0.8.14 INSTALLING THE VS CODE PLUGIN ----------------------------- - The VS Code plugin for Ballerina can be installed through the VS Code Marketplace. Look up "Ballerina" in the Marketplace and you should get the download page for the Ballerina plugin. ** Note **: For the VS Code plugin to work, the BALLERINA_HOME environment variable should be set and the /bin added to the path variable of the system. DOCUMENTATION ------------- The following resources should help you get up and running with Ballerina: - Ballerina by Example: https://ballerina.io/learn/by-example - Ballerina How-To guides: https://ballerina.io/learn/ - Ballerina standard library API docs: https://ballerina.io/learn/api-docs/ballerina - Ballerina language specification: https://ballerina.io/spec ================================================ FILE: resources/tools/LICENSE.txt ================================================ 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: resources/tools/PATENTS.txt ================================================ Additional IP Rights Grant (Patents) "This implementation" means the copyrightable works distributed by WSO2 as part of the Ballerina project. WSO2 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, transfer and otherwise run, modify and propagate the contents of this implementation of Ballerina, where such license applies only to those patent claims, both currently owned or controlled by WSO2 and acquired in the future, licensable by WSO2 that are necessarily infringed by this implementation of Ballerina. This grant does not include claims that would be infringed only as a consequence of further modification of this implementation. If you or your agent or exclusive licensee institute or order or agree to the institution of patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that this implementation of Ballerina or any code incorporated within this implementation of Ballerina constitutes direct or contributory patent infringement, or inducement of patent infringement, then any patent rights granted to you under this License for this implementation of Ballerina shall terminate as of the date such litigation is filed. ================================================ FILE: resources/tools/README.txt ================================================ ========= BALLERINA ========= DESCRIPTION ----------- Ballerina is an open source programming language and platform for cloud-era application programmers to easily write software that just works. INSTALLATION ------------ For information on how to install and set up Ballerina, refer to the INSTALL.txt file in the Ballerina home directory. USAGE ----- Once you have installed Ballerina, you can open a terminal and run the `bal` command to use Ballerina. Try running `bal version` to verify that everything is in order. You should see an output similar to the following: $ bal version jBallerina 1.1.0 Language specification 2019R3 Ballerina tool 0.8.0 Run `bal help` for more information on the commands and the options available. Check out our quick tour guide (https://ballerina.io/learn/quick-tour/) to get up and running with Ballerina in no time. SUPPORT ------- If you run in to any problems while using Ballerina, you can use the following mediums to raise your issues and get them resolved. - Discord server: https://discord.gg/ballerinalang - Stackoverflow: https://stackoverflow.com/questions/tagged/ballerina - Ballerina developer group: https://groups.google.com/forum/#!forum/ballerina-dev - Ballerina Github repository: https://github.com/ballerina-platform/ballerina-lang - Ballerina Twitter handle: https://twitter.com/ballerinalang ================================================ FILE: resources/tools/distributions/ballerina-version ================================================ ballerina-@version@ ================================================ FILE: resources/tools/distributions/installer-version ================================================ @uuid@ ================================================ FILE: resources/tools/scripts/install ================================================ #!/bin/bash # --------------------------------------------------------------------------- # Copyright (c) 2019, 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. if [ "$(uname)" == "Darwin" ] then CURRENT_PATH=$(dirname "$0") else CURRENT_PATH="$(dirname "$(readlink -f "$0")")" fi \cp $CURRENT_PATH/ballerina-command-@ballerinaCommandVersion@/lib/ballerina-command-@ballerinaCommandVersion@.jar $CURRENT_PATH/../lib if [ $? -ne '0' ]; then echo "error occurred while copying ballerina jar" # remove if copied with an error. if [ -f "$CURRENT_PATH/../lib/ballerina-command-@ballerinaCommandVersion@.jar" ]; then rm -rf $CURRENT_PATH/../lib/ballerina-command-@ballerinaCommandVersion@.jar fi exit $? fi \cp $CURRENT_PATH/ballerina-command-@ballerinaCommandVersion@/bin/bal $CURRENT_PATH/../bin if [ $? -ne '0' ]; then echo "error occurred while copying ballerina file." # remove already copied jar. if [ -f "$CURRENT_PATH/../lib/ballerina-command-@ballerinaCommandVersion@.jar" ]; then rm -rf $CURRENT_PATH/../lib/ballerina-command-@ballerinaCommandVersion@.jar fi exit $? fi echo "Tool version updated to the latest version: @ballerinaCommandVersion@" echo "Cleaning old files..." for file in $CURRENT_PATH/../lib/*; do if [ -f "$file" ] && [[ "$file" == *ballerina-command*.jar ]] && [[ "$file" != *ballerina-command-@ballerinaCommandVersion@.jar ]]; then rm -rf $file fi done ================================================ FILE: resources/tools/scripts/install.bat ================================================ @echo off REM --------------------------------------------------------------------------- REM Copyright (c) 2019, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. REM REM WSO2 Inc. licenses this file to you under the Apache License, REM Version 2.0 (the "License"); you may not use this file except REM 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, REM software distributed under the License is distributed on an REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY REM KIND, either express or implied. See the License for the REM specific language governing permissions and limitations REM under the License. REM --------------------------------------------------------------------------- setlocal set CURRENT_PATH=%~sdp0 xcopy /q %CURRENT_PATH%\ballerina-command-@ballerinaCommandVersion@\lib\ballerina-command-@ballerinaCommandVersion@.jar %CURRENT_PATH%\..\lib /Y if %errorlevel% neq 0 ( echo error occurred while copying ballerina jar REM remove if copied with an error. if exist %CURRENT_PATH%\..\lib\ballerina-command-@ballerinaCommandVersion@.jar ( del /F/Q %CURRENT_PATH%\..\lib\ballerina-command-@ballerinaCommandVersion@.jar ) exit /b %errorlevel% ) xcopy /q %CURRENT_PATH%\ballerina-command-@ballerinaCommandVersion@\bin\bal.bat %CURRENT_PATH%\..\bin /Y if %errorlevel% neq 0 ( echo error occurred while copying ballerina.bat REM remove if copied with an error. if exist %CURRENT_PATH%\..\lib\ballerina-command-@ballerinaCommandVersion@.jar ( del /F/Q %CURRENT_PATH%\..\lib\ballerina-command-@ballerinaCommandVersion@.jar ) exit /b %errorlevel% ) echo Tool version updated to the latest version: @ballerinaCommandVersion@ echo Cleaning old files... for %%f in (%CURRENT_PATH%\..\lib\*ballerina-command*.jar) do ( echo %%f|find /i "ballerina-command-@ballerinaCommandVersion@.jar">nul if errorlevel 1 ( del /F/Q "%%f" ) ) exit /b ================================================ FILE: settings.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 "com.gradle.enterprise" version "3.13.2" } rootProject.name = 'ballerina-distribution' include ':ballerina' include ':ballerina-distribution-test' include ':devtools-integration-tests' include ':project-api-tests' include 'dist-repo-builder' include 'build-time-tests' include 'cache-generator' include 'config:checkstyle' project(':ballerina').projectDir = "$rootDir/ballerina" as File project(':ballerina-distribution-test').projectDir = "$rootDir/ballerina-test" as File project(':dist-repo-builder').projectDir = "$rootDir/dist-repo-builder" as File project(':cache-generator').projectDir = "$rootDir/cache-generator" as File project(':config').projectDir = "$rootDir config" as File gradleEnterprise { buildScan { termsOfServiceUrl = 'https://gradle.com/terms-of-service' termsOfServiceAgree = 'yes' } } include 'language-server-simulator' ================================================ FILE: stdlib-integration-tests/auth/tests/Config.toml ================================================ [[ballerina.auth.users]] username="alice" password="alice@123" scopes=["admin", "developer"] [[ballerina.auth.users]] username="bob" password="bob@123" scopes=["developer"] ================================================ FILE: stdlib-integration-tests/auth/tests/auth_test.bal ================================================ // Copyright (c) 2021 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. // // WSO2 Inc. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except // in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. import ballerina/http; import ballerina/test; listener http:Listener authListener = new(25001, { secureSocket: { key: { path: "tests/resources/keystore/ballerinaKeystore.p12", password: "ballerina" } } }); @http:ServiceConfig { auth: [ { fileUserStoreConfig: {}, scopes: ["admin"] } ] } service /foo on authListener { resource function get bar() returns string { return "Hello World!"; } } @test:Config {} public function testAuthModuleFileUserStore1() { http:Client clientEP = checkpanic new("https://localhost:25001", { secureSocket: { cert: { path: "tests/resources/keystore/ballerinaTruststore.p12", password: "ballerina" } }, auth: { username: "alice", password: "alice@123" } }); http:Response|http:ClientError response = clientEP->get("/foo/bar"); if (response is http:Response) { assertOK(response); } else { test:assertFail(msg = "Test Failed!"); } } @test:Config {} public function testAuthModuleFileUserStore2() { http:Client clientEP = checkpanic new("https://localhost:25001", { secureSocket: { cert: { path: "tests/resources/keystore/ballerinaTruststore.p12", password: "ballerina" } }, auth: { username: "bob", password: "bob@123" } }); http:Response|http:ClientError response = clientEP->get("/foo/bar"); if (response is http:Response) { assertForbidden(response); } else { test:assertFail(msg = "Test Failed!"); } } @test:Config {} public function testAuthModuleFileUserStore3() { http:Client clientEP = checkpanic new("https://localhost:25001", { secureSocket: { cert: { path: "tests/resources/keystore/ballerinaTruststore.p12", password: "ballerina" } }, auth: { username: "eve", password: "eve@123" } }); http:Response|http:ClientError response = clientEP->get("/foo/bar"); if (response is http:Response) { assertUnauthorized(response); } else { test:assertFail(msg = "Test Failed!"); } } function assertOK(http:Response res) { test:assertEquals(res.statusCode, http:STATUS_OK, msg = "Response code mismatched"); } function assertUnauthorized(http:Response res) { test:assertEquals(res.statusCode, http:STATUS_UNAUTHORIZED, msg = "Response code mismatched"); } function assertForbidden(http:Response res) { test:assertEquals(res.statusCode, http:STATUS_FORBIDDEN, msg = "Response code mismatched"); } ================================================ FILE: stdlib-integration-tests/crypto/tests/crypto_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/crypto; import ballerina/test; @test:Config {} function testDecodePrivateKey() { crypto:KeyStore keyStore = { path: "tests/resources/keystore/ballerinaKeystore.p12", password: "ballerina" }; var result = crypto:decodeRsaPrivateKeyFromKeyStore(keyStore, "ballerina", "ballerina"); if (result is crypto:PrivateKey) { test:assertEquals(result.algorithm, "RSA", msg = "Invalid algorithm of decoded private key."); } else { test:assertFail(msg = "Test Failed! " + result.message()); } } @test:Config {} function testDecodePublicKey() { crypto:TrustStore truststore = { path: "tests/resources/keystore/ballerinaTruststore.p12", password: "ballerina" }; var result = crypto:decodeRsaPublicKeyFromTrustStore(truststore, "ballerina"); if (result is crypto:PublicKey) { test:assertEquals(result.algorithm, "RSA", msg = "Invalid algorithm of decoded public key."); } else { test:assertFail(msg = "Test Failed! " + result.message()); } } ================================================ FILE: stdlib-integration-tests/email/tests/email_test.bal ================================================ import ballerina/email; import ballerina/test; @test:Config {} function testEmail() returns @tainted error?{ email:SmtpClient smtpClient = check new ("smtp.email.com", "sender@email.com" , "pass123"); email:Message email = { to: ["receiver1@email.com", "receiver2@email.com"], cc: ["receiver3@email.com", "receiver4@email.com"], bcc: ["receiver5@email.com"], subject: "Sample Email", body: "This is a sample email.", 'from: "author@email.com", sender: "sender@email.com", replyTo: ["replyTo1@email.com", "replyTo2@email.com"] }; test:assertEquals(email.subject, "Sample Email"); } ================================================ FILE: stdlib-integration-tests/ftp/tests/ftp_test.bal ================================================ import ballerina/ftp; import ballerina/test; @test:Config {} function testFtp() { ftp:ClientEndpointConfig config = { protocol: ftp:FTP, host: "127.0.0.1", port: 21212, secureSocket: { basicAuth: { username: "wso2", password: "wso2123" } } }; test:assertEquals(config.host, "127.0.0.1"); } ================================================ FILE: stdlib-integration-tests/http/tests/http_1_1_passthrough_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/http; import ballerina/lang.'string as strings; import ballerina/mime; import ballerina/test; listener http:Listener passthroughEP1 = new(9113); service http:Service /passthrough on passthroughEP1 { resource function get .(http:Request clientRequest) returns http:Response|http:InternalServerError|error { http:Client nyseEP1 = check new("http://localhost:9113"); http:Response|error response = nyseEP1->get("/nyseStock/stocks"); if (response is http:Response) { return response; } else { return { body: "error occurred while invoking the service" }; } } resource function post forwardMultipart(http:Request clientRequest) returns http:Response|http:InternalServerError|error { http:Client nyseEP1 = check new("http://localhost:9113"); http:Response|error response = nyseEP1->forward("/nyseStock/stocksAsMultiparts", clientRequest); if (response is http:Response) { return response; } else { return { body: "error occurred while invoking the service" }; } } resource function post forward(http:Request clientRequest) returns http:Ok|http:InternalServerError|error { http:Client nyseEP1 = check new("http://localhost:9113"); http:Response|error response = nyseEP1->forward("/nyseStock/entityCheck", clientRequest); if (response is http:Response) { var entity = response.getEntity(); if (entity is mime:Entity) { string|error payload = entity.getText(); if (payload is string) { http:Ok ok = {body: payload + ", " + checkpanic entity.getHeader("X-check-header")}; return ok; } else { http:InternalServerError err = {body: payload.toString()}; return err; } } else { http:InternalServerError err = {body: entity.toString()}; return err; } } else { http:InternalServerError err = {body: response.toString()}; return err; } } } service http:Service /nyseStock on passthroughEP1 { resource function get stocks() returns json { return { "exchange": "nyse", "name": "IBM", "value": "127.50" }; } resource function post stocksAsMultiparts(http:Caller caller, http:Request clientRequest) returns error? { mime:Entity[] bodyParts = check clientRequest.getBodyParts(); check caller->respond(bodyParts); } resource function post entityCheck(http:Request clientRequest) returns http:Response|http:InternalServerError|error? { http:Response res = new; var entity = clientRequest.getEntity(); if (entity is mime:Entity) { json|error textPayload = entity.getText(); if (textPayload is string) { mime:Entity mimeEntity = new; mimeEntity.setText("payload :" + textPayload + ", header: " + check entity.getHeader("Content-type")); mimeEntity.setHeader("X-check-header", "entity-check-header"); res.setEntity(mimeEntity); return res; } return {body: "Error while retrieving from entity"}; } return {body: "Error while retrieving from request"}; } } @test:Config {} public function testPassthroughServiceByBasePath() returns error? { http:Client httpClient = check new("http://localhost:9113"); json resp = check httpClient->get("/passthrough"); test:assertEquals(resp, {"exchange":"nyse", "name":"IBM", "value":"127.50"}); } @test:Config {} public function testPassthroughServiceWithMimeEntity() returns error? { http:Client httpClient = checkpanic new("http://localhost:9113"); string resp = check httpClient->post("/passthrough/forward", "Hello from POST!"); test:assertEquals(resp, "payload :Hello from POST!, header: text/plain, entity-check-header"); } @test:Config {} public function testPassthroughWithMultiparts() { http:Client httpClient = checkpanic new("http://localhost:9113"); mime:Entity textPart1 = new; textPart1.setText("Part1"); textPart1.setHeader("Content-Type", "text/plain; charset=UTF-8"); mime:Entity textPart2 = new; textPart2.setText("Part2"); textPart2.setHeader("Content-Type", "text/plain"); mime:Entity[] bodyParts = [textPart1, textPart2]; http:Request request = new; request.setBodyParts(bodyParts, contentType = mime:MULTIPART_FORM_DATA); http:Response|error resp = httpClient->post("/passthrough/forwardMultipart", request); if (resp is http:Response) { string contentType = checkpanic resp.getHeader("content-type"); test:assertTrue(strings:includes(contentType, "multipart/form-data")); var respBodyParts = resp.getBodyParts(); if (respBodyParts is mime:Entity[]) { test:assertEquals(respBodyParts.length(), 2); string|error textPart = respBodyParts[0].getText(); if (textPart is string) { test:assertEquals(textPart, "Part1"); } else { test:assertFail(msg = "Found an unexpected output: " + textPart.message()); } string|error txtPart2 = respBodyParts[1].getText(); if (txtPart2 is string) { test:assertEquals(txtPart2, "Part2"); } else { test:assertFail(msg = "Found an unexpected output: " + txtPart2.message()); } } } else { test:assertFail(msg = "Found unexpected output: " + resp.message()); } } ================================================ FILE: stdlib-integration-tests/http/tests/http_2_0_server_push_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/http; import ballerina/io; import ballerina/lang.'string as strings; import ballerina/test; listener http:Listener serverPushFrontendEP = new(9115); listener http:Listener serverPushBackendEP = new(9116, { httpVersion: "2.0" }); http:Client serverPushClient = checkpanic new("http://localhost:9115"); http:Client backendClientEP = checkpanic new("http://localhost:9116", { httpVersion: "2.0" }); service /frontend on serverPushFrontendEP { resource function get .(http:Caller caller, http:Request clientRequest) { http:Request serviceReq = new; http:HttpFuture httpFuture = new; // Submit a request http:HttpFuture|http:ClientError submissionResult = backendClientEP->submit("GET", "/backend/main", serviceReq); if (submissionResult is http:HttpFuture) { httpFuture = submissionResult; } else { io:println("Error occurred while submitting a request"); json errMsg = { "error": "error occurred while submitting a request" }; checkpanic caller->respond(errMsg); return; } // Check whether promises exists http:PushPromise?[] promises = []; int promiseCount = 0; boolean hasPromise = backendClientEP->hasPromise(httpFuture); while (hasPromise) { http:PushPromise pushPromise = new; // Get the next promise http:PushPromise|http:ClientError nextPromiseResult = backendClientEP->getNextPromise(httpFuture); if (nextPromiseResult is http:PushPromise) { pushPromise = nextPromiseResult; } else { io:println("Error occurred while fetching a push promise"); json errMsg = { "error": "error occurred while fetching a push promise" }; checkpanic caller->respond(errMsg); return; } io:println("Received a promise for " + pushPromise.path); // Store required promises promises[promiseCount] = pushPromise; promiseCount = promiseCount + 1; hasPromise = backendClientEP->hasPromise(httpFuture); } // By this time 3 promises should be received, if not send an error response if (promiseCount != 3) { json errMsg = { "error": "expected number of promises not received" }; checkpanic caller->respond(errMsg); return; } io:println("Number of promises received : " + promiseCount.toString()); // Get the requested resource http:Response response = new; http:Response|http:ClientError result = backendClientEP->getResponse(httpFuture); if (result is http:Response) { response = result; } else { io:println("Error occurred while fetching response"); json errMsg = { "error": "error occurred while fetching response" }; checkpanic caller->respond(errMsg); return; } var responsePayload = response.getJsonPayload(); json responseJsonPayload = {}; if (responsePayload is json) { responseJsonPayload = responsePayload; } else { json errMsg = { "error": "expected response message not received" }; checkpanic caller->respond(errMsg); return; } // Check whether correct response received string responseStringPayload = responseJsonPayload.toString(); if (!(strings:includes(responseStringPayload, "main"))) { json errMsg = { "error": "expected response message not received" }; checkpanic caller->respond(errMsg); return; } io:println("Response : " + responseStringPayload); // Fetch required promised responses foreach var p in promises { http:PushPromise promise = p; http:Response promisedResponse = new; http:Response|http:ClientError promisedResponseResult = backendClientEP->getPromisedResponse(promise); if (promisedResponseResult is http:Response) { promisedResponse = promisedResponseResult; } else { io:println("Error occurred while fetching promised response"); json errMsg = { "error": "error occurred while fetching promised response" }; checkpanic caller->respond(errMsg); return; } json promisedJsonPayload = {}; var promisedPayload = promisedResponse.getJsonPayload(); if (promisedPayload is json) { promisedJsonPayload = promisedPayload; } else { json errMsg = { "error": "expected promised response not received" }; checkpanic caller->respond(errMsg); return; } // check whether expected string expectedVal = promise.path.substring(1, 10); string promisedStringPayload = promisedJsonPayload.toString(); if (!(strings:includes(promisedStringPayload, expectedVal))) { json errMsg = { "error": "expected promised response not received" }; checkpanic caller->respond(errMsg); return; } io:println("Promised resource : " + promisedStringPayload); } // By this time everything has went well, hence send a success response json successMsg = { "status": "successful" }; checkpanic caller->respond(successMsg); } } service /backend on serverPushBackendEP { resource function get main(http:Caller caller, http:Request req) { io:println("Request received"); // Send a Push Promise http:PushPromise promise1 = new("/resource1", "POST"); checkpanic caller->promise(promise1); // Send another Push Promise http:PushPromise promise2 = new("/resource2", "POST"); checkpanic caller->promise(promise2); // Send one more Push Promise http:PushPromise promise3 = new; // create with default params promise3.path = "/resource3"; // set parameters promise3.method = "POST"; checkpanic caller->promise(promise3); // Construct requested resource json msg = { "response": { "name": "main resource" } }; // Send the requested resource checkpanic caller->respond(msg); // Construct promised resource1 http:Response push1 = new; msg = { "push": { "name": "resource1" } }; push1.setJsonPayload(msg); // Push promised resource1 checkpanic caller->pushPromisedResponse(promise1, push1); http:Response push2 = new; msg = { "push": { "name": "resource2" } }; push2.setJsonPayload(msg); // Push promised resource2 checkpanic caller->pushPromisedResponse(promise2, push2); http:Response push3 = new; msg = { "push": { "name": "resource3" } }; push3.setJsonPayload(msg); // Push promised resource3 checkpanic caller->pushPromisedResponse(promise3, push3); } } //Test HTTP/2.0 Server Push scenario @test:Config {} function testPushPromise() { http:Response|error response = serverPushClient->get("/frontend"); if (response is http:Response) { test:assertEquals(response.statusCode, 200, msg = "Found unexpected output"); test:assertEquals(checkpanic response.getHeader("content-type"), "application/json"); var payload = response.getJsonPayload(); if payload is json { test:assertEquals(payload, {status:"successful"}, msg = "Found unexpected output"); } else { test:assertFail(msg = "Found unexpected output type: " + payload.message()); } } else { test:assertFail(msg = "Found unexpected output type: " + (response).message()); } } ================================================ FILE: stdlib-integration-tests/http/tests/rest_introspection_test.bal ================================================ // Copyright (c) 2021 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. // // WSO2 Inc. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except // in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. import ballerina/http; import ballerina/openapi; import ballerina/test; listener http:Listener restIntrospectionEP = new(9118); @openapi:ServiceInfo { embed: true } service /hello on restIntrospectionEP { resource function get world() returns string { return "Hello, World!"; } } http:Client restApiClient = checkpanic new("http://localhost:9118"); //Test Introspection Resource availability @test:Config { enable: false } function testIntrospectionResourceAvailability() returns error? { http:Response response = check restApiClient->options("/hello"); test:assertEquals(response.statusCode, 204); test:assertTrue(response.hasHeader("Link"), "Could not find the Link header"); string linkHeader = check response.getHeader("Link"); test:assertEquals(linkHeader, ";rel=\"service-desc\""); } json openApiDocumentation = { "openapi" : "3.0.1", "info" : { "title" : "Hello", "version" : "1.0.0" }, "servers" : [ { "url" : "{server}:{port}/hello", "variables" : { "server" : { "default" : "http://localhost" }, "port" : { "default" : "9118" } } } ], "paths" : { "/world" : { "get" : { "operationId" : "operation_get_/world", "responses" : { "200" : { "description" : "Ok", "content" : { "text/plain" : { "schema" : { "type" : "string" } } } } } } } }, "components" : { } }; //Test REST API Doc @test:Config { enable: false } function testRestApiDoc() returns error? { json receivedApiDoc = check restApiClient->get("/hello/openapi-doc-dygixywsw"); test:assertEquals(receivedApiDoc, openApiDocumentation); } service /disabled on restIntrospectionEP { resource function get world() returns string { return "Hello, World!"; } } @test:Config {} function testUnavailableIntrospectionResource() returns error? { http:Response response = check restApiClient->options("/disabled"); test:assertEquals(response.statusCode, 204); test:assertFalse(response.hasHeader("Link"), "Found link header in errorneous scenario"); } ================================================ FILE: stdlib-integration-tests/index.json ================================================ [ { "name": "TCP standard library ", "path": "tcp", "enableTest": true }, { "name": "Email standard library ", "path": "email", "enableTest": true }, { "name": "URL standard library ", "path": "url", "enableTest": true }, { "name": "Crypto standard library ", "path": "crypto", "enableTest": true }, { "name": "Auth standard library ", "path": "auth", "enableTest": true }, { "name": "JWT standard library ", "path": "jwt", "enableTest": true }, { "name": "Task standard library ", "path": "task", "enableTest": true }, { "name": "OAuth2 standard library ", "path": "oauth2", "enableTest": true }, { "name": "HTTP standard library", "path": "http", "enableTest": true }, { "name": "MIME standard library", "path": "mime", "enableTest": true }, { "name": "Websub start hub and register topic", "path": "websub", "enableTest": false }, { "name": "UDP standard library ", "path": "udp", "enableTest": true }, { "name": "WebSocket standard library", "path": "websocket", "enableTest": true }, { "name": "Transaction standard library", "path": "transaction", "enableTest": true }, { "name": "Library package test", "path": "library_package_test", "enableTest": true } ] ================================================ FILE: stdlib-integration-tests/jwt/tests/jwt_test.bal ================================================ // Copyright (c) 2021 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. // // WSO2 Inc. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except // in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. import ballerina/http; import ballerina/test; listener http:Listener jwtListener = new(25002, { secureSocket: { key: { path: "tests/resources/keystore/ballerinaKeystore.p12", password: "ballerina" } } }); @http:ServiceConfig { auth: [ { scopes: ["write", "update"], jwtValidatorConfig: { issuer: "wso2", audience: "ballerina", signatureConfig: { trustStoreConfig: { trustStore: { path: "tests/resources/keystore/ballerinaTruststore.p12", password: "ballerina" }, certAlias: "ballerina" } }, scopeKey: "scp" } } ] } service /foo on jwtListener { resource function get bar() returns string { return "Hello World!"; } } const string JWT = "eyJhbGciOiJSUzI1NiIsICJ0eXAiOiJKV1QiLCAia2lkIjoiTlRBeFptTXhORE15WkRnM01UVTFaR00wTXpFek9ESmhaV0k" + "0TkRObFpEVTFPR0ZrTmpGaU1RIn0.eyJzdWIiOiJhZG1pbiIsICJpc3MiOiJ3c28yIiwgImV4cCI6MTkyNTk1NTcyNCwgIm" + "p0aSI6IjEwMDA3ODIzNGJhMjMiLCAiYXVkIjpbImJhbGxlcmluYSJdLCAic2NwIjoid3JpdGUifQ.H99ufLvCLFA5i1gfCt" + "klVdPrBvEl96aobNvtpEaCsO4v6_EgEZYz8Pg0B1Y7yJPbgpuAzXEg_CzowtfCTu3jUFf5FH_6M1fWGko5vpljtCb5Xknt_" + "YPqvbk5fJbifKeXqbkCGfM9c0GS0uQO5ss8StquQcofxNgvImRV5eEGcDdybkKBNkbA-sJFHd1jEhb8rMdT0M0SZFLnhrPL" + "8edbFZ-oa-ffLLls0vlEjUA7JiOSpnMbxRmT-ac6QjPxTQgNcndvIZVP2BHueQ1upyNorFKSMv8HZpATYHZjgnJQSpmt3Oa" + "oFJ6pgzbFuniVNuqYghikCQIizqzQNfC7JUD8wA"; @test:Config {} public function testJwtModule() { http:Client clientEP = checkpanic new("https://localhost:25002", { secureSocket: { cert: { path: "tests/resources/keystore/ballerinaTruststore.p12", password: "ballerina" } }, auth: { token: JWT } }); http:Response|http:ClientError response = clientEP->get("/foo/bar"); if (response is http:Response) { assertOK(response); } else { test:assertFail(msg = "Test Failed!"); } } function assertOK(http:Response res) { test:assertEquals(res.statusCode, http:STATUS_OK, msg = "Response code mismatched"); } ================================================ FILE: stdlib-integration-tests/library_package_test/Ballerina.toml ================================================ [package] org = "wso2" name = "test_package" version = "0.1.0" [build-options] offline = true ================================================ FILE: stdlib-integration-tests/library_package_test/main.bal ================================================ // Copyright (c) 2026 WSO2 LLC. (http://www.wso2.com) // // WSO2 LLC. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except // in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. import ballerina/io as _; import ballerina/jballerina.java.arrays as _; import ballerina/math.vector as _; import ballerina/persist as _; import ballerina/time as _; import ballerina/url as _; import ballerina/avro as _; import ballerina/constraint as _; import ballerina/crypto as _; import ballerina/log as _; import ballerina/os as _; import ballerina/protobuf as _; import ballerina/random as _; import ballerina/xslt as _; import ballerina/data.yaml as _; import ballerina/file as _; import ballerina/ldap as _; import ballerina/mime as _; import ballerina/tcp as _; import ballerina/udp as _; import ballerina/uuid as _; import ballerina/data.csv as _; import ballerina/data.jsondata as _; import ballerina/data.xmldata as _; import ballerina/edi as _; import ballerina/mqtt as _; import ballerina/task as _; import ballerina/toml as _; import ballerina/yaml as _; import ballerina/cache as _; import ballerina/email as _; import ballerina/ftp as _; import ballerina/messaging as _; import ballerina/auth as _; import ballerina/jwt as _; import ballerina/oauth2 as _; import ballerina/http as _; import ballerina/ai as _; import ballerina/grpc as _; import ballerina/soap as _; import ballerina/websocket as _; import ballerina/websub as _; import ballerina/websubhub as _; import ballerina/ai.np as _; import ballerina/graphql as _; import ballerina/sql as _; import ballerina/openapi as _; import ballerina/mcp as _; ================================================ FILE: stdlib-integration-tests/mime/tests/mime_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/mime; import ballerina/test; @test:Config {} function testMimeFunctions() { mime:Entity entity = new; string entityBody = "Hello Ballerina!"; entity.setBody(entityBody); var stringPayload = entity.getText(); if stringPayload is string { test:assertEquals(stringPayload, entityBody, msg = "Found unexpected output"); } else { test:assertFail(msg = "Found unexpected output type"); } entity = new; json jsonContent = {code:123}; entity.setHeader("content-type", "application/yang-patch+json"); entity.setJson(jsonContent); var JsonPayload = entity.getJson(); if JsonPayload is json { test:assertEquals(JsonPayload, jsonContent, msg = "Found unexpected output"); } else { test:assertFail(msg = "Found unexpected output type"); } string headerName = "Content-Type"; string headerValue = "application/json"; string headerNameToBeUsedForRetrieval = "Content-Type"; entity = new; entity.addHeader(headerName, headerValue); string returnVal = checkpanic entity.getHeader(headerNameToBeUsedForRetrieval); test:assertEquals(returnVal, headerValue, msg = "Found unexpected output"); } ================================================ FILE: stdlib-integration-tests/oauth2/tests/oauth2_test.bal ================================================ // Copyright (c) 2021 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. // // WSO2 Inc. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except // in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. import ballerina/http; import ballerina/test; listener http:Listener oauth2Listener = new(25003, { secureSocket: { key: { path: "tests/resources/keystore/ballerinaKeystore.p12", password: "ballerina" } } }); @http:ServiceConfig { auth: [ { scopes: ["write", "update"], oauth2IntrospectionConfig: { url: "https://localhost:25999/oauth2/token/introspect", tokenTypeHint: "access_token", scopeKey: "scp", clientConfig: { secureSocket: { cert: { path: "tests/resources/keystore/ballerinaTruststore.p12", password: "ballerina" } } } } } ] } service /foo on oauth2Listener { resource function get bar() returns string { return "Hello World!"; } } const string ACCESS_TOKEN = "2YotnFZFEjr1zCsicMWpAA"; @test:Config {} public function testOAuth2Module() { http:Client clientEP = checkpanic new("https://localhost:25003", { secureSocket: { cert: { path: "tests/resources/keystore/ballerinaTruststore.p12", password: "ballerina" } }, auth: { token: ACCESS_TOKEN } }); http:Response|http:ClientError response = clientEP->get("/foo/bar"); if (response is http:Response) { assertOK(response); } else { test:assertFail(msg = "Test Failed!"); } } function assertOK(http:Response res) { test:assertEquals(res.statusCode, http:STATUS_OK, msg = "Response code mismatched"); } // Mock OAuth2 authorization server implementation, which treats the APIs with successful responses. listener http:Listener authorizationServer = new(25999, { secureSocket: { key: { path: "tests/resources/keystore/ballerinaKeystore.p12", password: "ballerina" } } }); service /oauth2 on authorizationServer { resource function post token/introspect(http:Request request) returns json { string|http:ClientError payload = request.getTextPayload(); if (payload is string) { string[] parts = re `&`.split(payload); foreach string part in parts { if (part.indexOf("token=") is int) { string token = re `=`.split(part)[1]; if (token == ACCESS_TOKEN) { json response = { "active": true, "exp": 3600, "scp": "read write" }; return response; } else { json response = { "active": false }; return response; } } } } } } ================================================ FILE: stdlib-integration-tests/task/tests/http_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/lang.runtime; import ballerina/task; import ballerina/test; import ballerina/time; const HTTP_MESSAGE = "Hello from http service"; const TASK_MESSAGE = "Hello from task service"; http:Client httpClient = check new ("http://localhost:15001/HttpService"); listener http:Listener backEndListener = new (15001); listener http:Listener clientListener = new (15002); string http_payload = ""; service / on clientListener { resource function get .(http:Caller caller, http:Request request) returns error? { var result = check caller->respond(http_payload); } } service /HttpService on backEndListener { resource function post .(http:Caller caller, http:Request request) { http:Response response = new; var requestMessage = request.getTextPayload(); if (requestMessage is error) { response.statusCode = 501; response.setTextPayload("[HTTP Service] Failed to retrieve the request"); } else { if (requestMessage == TASK_MESSAGE) { response.statusCode = 200; response.setTextPayload(HTTP_MESSAGE); } else { response.statusCode = 400; response.setTextPayload("[HTTP Service] Invalid Request Message Received"); } } var result = caller->respond(response); if (result is http:ListenerError) { panic result; } } } @test:Config {} function testTaskWithHttpClient() returns error? { http:Client multipleAttachmentClientEndpoint = check new ("http://localhost:15002"); time:Utc currentUtc = time:utcNow(); time:Utc newTime = time:utcAddSeconds(currentUtc, 1); time:Civil time = time:utcToCivil(newTime); task:JobId id = check task:scheduleJobRecurByFrequency(new Job(), 1, startTime = time); runtime:sleep(4); http:Response|http:ClientError response = multipleAttachmentClientEndpoint->get("/"); if (response is http:Response) { test:assertEquals(response.getTextPayload(), HTTP_MESSAGE, msg = "Response payload mismatched"); } else { test:assertFail(msg = (response).message()); } } class Job { *task:Job; public function execute() { http:Request request = new; request.setTextPayload(TASK_MESSAGE); http:Response|http:ClientError result = httpClient->post("/", request); if (result is http:ClientError) { panic result; } else { if (result.statusCode == 200) { var payload = result.getTextPayload(); if (payload is http:ClientError) { panic payload; } else { http_payload = <@untainted string>payload; } } } } } ================================================ FILE: stdlib-integration-tests/tcp/tests/mock_servers.bal ================================================ // Copyright (c) 2021 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. // // WSO2 Inc. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except // in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. import ballerina/io; import ballerina/tcp; const int PORT1 = 8809; const int PORT2 = 8023; const int PORT3 = 8639; const int PORT4 = 8475; string keyPath = "tests/resources/private.key"; string certPath = "tests/resources/public.crt"; listener tcp:Listener echoServer = new tcp:Listener(PORT1); listener tcp:Listener discardServer = new tcp:Listener(PORT2); listener tcp:Listener closeServer = new tcp:Listener(PORT3); service on echoServer { isolated remote function onConnect(tcp:Caller caller) returns tcp:ConnectionService { io:println("Client connected to echoServer: ", caller.remotePort); return new EchoService(); } } service class EchoService { *tcp:ConnectionService; remote function onBytes(tcp:Caller caller, readonly & byte[] data) returns tcp:Error? { io:println("Echo: ", 'string:fromBytes(data)); return caller->writeBytes(data); } isolated remote function onError(tcp:Error err) { io:println(err.message()); } isolated remote function onClose() { io:println("invoke on close"); } } service on discardServer { isolated remote function onConnect(tcp:Caller caller) returns tcp:ConnectionService { io:println("Client connected to discardServer: ", caller.remotePort); return new DiscardService(); } } service class DiscardService { *tcp:ConnectionService; remote function onBytes(readonly & byte[] data) { // read and discard the message io:println("Discard: ", 'string:fromBytes(data)); } } service on closeServer { isolated remote function onConnect(tcp:Caller caller) returns tcp:Error? { io:println("Client connected to closeServer: ", caller.remotePort); return caller->close(); } } service on new tcp:Listener(PORT4, secureSocket = { key: { certFile: certPath, keyFile: keyPath }, protocol: { name: tcp:TLS, versions: ["TLSv1.2", "TLSv1.1"] }, ciphers: ["TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"], handshakeTimeout: 10 }, localHost = "localhost") { isolated remote function onConnect(tcp:Caller caller) returns tcp:ConnectionService { io:println("Client connected to secureEchoServer: ", caller.remotePort); return new SecureEchoService(); } } service class SecureEchoService { *tcp:ConnectionService; remote function onBytes(readonly & byte[] data) returns byte[] { io:println("Echo: ", 'string:fromBytes(data)); return data; } isolated remote function onError(tcp:Error err) { io:println(err.message()); } } ================================================ FILE: stdlib-integration-tests/tcp/tests/resources/private.key ================================================ Bag Attributes friendlyName: client localKeyID: 54 69 6D 65 20 31 36 31 31 37 32 31 38 36 39 38 38 36 Key Attributes: -----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCLcLXa8m82YOJV 8zKL9g6SkO66+1Vk+pGRdHoMWYhaXcg2Vc96NI+INATifZMe/xVd3sp286nOXR/T hKfHb1gTqmqSguhsbGJ0yl/ShO7Qwu1AB0TgMVef6izb/CiWUqrHQeJ5TzVBas6Q GR1Zc5jDDM9SqjTUitxdGRBMJ+n/CW+JICvqnxn1PVhZIadRksAyv+5P9xBg0EU7 N6rbNZOTUOwxi01+D8nyW6TDz9SRW6GoaEjek5GLzSkRullAbVuoyqqlyT7SqMFP R++jRvwTq+zq0QLorVMtf1J0vdVpFR4uJRp3ZsHpxiGoJpltXsE9OjPvkVQUpTWV 7Rb89ueNAgMBAAECggEBAIVZSnCSPEzSpmDv7LzwmMsNk2B5jxa8kY16yGUbEkzp NtjPg1UpAsKMjMdUs+eGphZZQ2Iyb4eMgy0yxYjyMFbOOJDYZBGcLSPnjW+z9Pbs ExuSWAnW5idmOXQ/V0k60Vsw5LaCSzm+PMCNZAXxiHuvum0hnNhN26OknnNWEG72 W7clr75VRnFVFqdoCOudLdbef0+HG/U4y0DSoLQByxD8kNr8FDB9jT8vbHXeqvfr 3VfJCHIx1blDOi1glxLmiy5e79RWrPD2hJE930tvfgC1y9kcGtZvAZHKeJn4PgO6 F9Nd5OYW/vWwYssZe6VHIJybuzAOhewxxZCxIMVik0ECgYEAvS4S4CJu8R3E0hyQ CCZEP9O5Jq8Ja2O2WjVUvbG9vdbCq68/HwSv3c6RlejbYIIZinL4BiIf9UXspjUh LPyEwUMKkbFwuDYEdAr5re83nVHJkCdHzXOs66S1/DhA/DQmd9dZdHzKh2kKXmZY bVz9eGAth9fkyANfwuvZSOadYUUCgYEAvLEZ5cW7sgigi6zggLUa8o+VSrAa5Wbj 2hsksNyzFx6+VCZTA7B8TbGtqcN8+XxUp3qwEd7summ8dFFmM/y/bgKZgU2ogXuq K1UsZJSrqJLtm/DuxwPAdrF2BnICW9M8UyuIQlJ+3SqTMTWM43gHRJ3VmqboIj+G 4GAmoyn3fakCgYBh1Ii4VbYvhGh7C4BEZp2WsEOoSI5Dda4gWkH3Uwo/OOc054xp 2v+jYaaR8ng4IS9aFHhEm7MRINbvYLxGSlPQqX22uB8FdBG7fM1/cBy/g0MMA/Qy v+YleekOhHy+r61Q/hFplvsZKW3OQXjrHVKKxJnnDPNnp68G2vZjp7atTQKBgGNa rVQ7RZV8zWw+XwfmVIYb7HQjPkZEYv1ZBXzK+NHXskgE1fk/WHRgdhzJfmiverOk +kiupN+TBVQJ+FKvZuy6GbHn/i4Pu46Njo53adudYdeWiWPpKdJVptvF9E/9beot JdYmMf6qiE6E7+ZmMQ2EPhDsiZZe9Nn4Rt/rHRHhAoGABjXw6CZpTCWjE5aWABN4 QBlzGXYt586UCl20v6qWs+DbjSL25BV0oXycBrUe5Mmcx2mN3zczWhcL2xGFe0vY FsBolkd0UrDKVDlt2IxR7FEYAjYCiTROqtGVC/4liaU4WPOXj2xzywpAqQh0onC8 mk9VOWz0I9cxMx64mQ+blm8= -----END PRIVATE KEY----- ================================================ FILE: stdlib-integration-tests/tcp/tests/resources/public.crt ================================================ Bag Attributes friendlyName: client localKeyID: 54 69 6D 65 20 31 36 31 31 37 32 31 38 36 39 38 38 36 subject=C = Unknown, ST = Unknown, L = Unknown, O = Unknown, OU = Unknown, CN = Unknown issuer=C = Unknown, ST = Unknown, L = Unknown, O = Unknown, OU = Unknown, CN = Unknown -----BEGIN CERTIFICATE----- MIIDdzCCAl+gAwIBAgIEWN11KDANBgkqhkiG9w0BAQsFADBsMRAwDgYDVQQGEwdV bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD VQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRAwDgYDVQQDEwdVbmtub3du MB4XDTIxMDEyNzA0MjkyNFoXDTIxMTEyMzA0MjkyNFowbDEQMA4GA1UEBhMHVW5r bm93bjEQMA4GA1UECBMHVW5rbm93bjEQMA4GA1UEBxMHVW5rbm93bjEQMA4GA1UE ChMHVW5rbm93bjEQMA4GA1UECxMHVW5rbm93bjEQMA4GA1UEAxMHVW5rbm93bjCC ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAItwtdrybzZg4lXzMov2DpKQ 7rr7VWT6kZF0egxZiFpdyDZVz3o0j4g0BOJ9kx7/FV3eynbzqc5dH9OEp8dvWBOq apKC6GxsYnTKX9KE7tDC7UAHROAxV5/qLNv8KJZSqsdB4nlPNUFqzpAZHVlzmMMM z1KqNNSK3F0ZEEwn6f8Jb4kgK+qfGfU9WFkhp1GSwDK/7k/3EGDQRTs3qts1k5NQ 7DGLTX4PyfJbpMPP1JFboahoSN6TkYvNKRG6WUBtW6jKqqXJPtKowU9H76NG/BOr 7OrRAuitUy1/UnS91WkVHi4lGndmwenGIagmmW1ewT06M++RVBSlNZXtFvz2540C AwEAAaMhMB8wHQYDVR0OBBYEFEMEVsRz0vAu51x15LYY9bssZduHMA0GCSqGSIb3 DQEBCwUAA4IBAQATFjwbhsPMBMBfnK1WKZ4qzW+0PieH5vFbV57bULkUaI6p0zSO jQvOh3H8FRI06MaOolr40N/Mj/BkYjbYmYnSRTttRN5msgx6gcA+vUeo/j3ui3kI RD8NZjrwrFLfO3GsrfMnDzZWI2dNHjjAH3AI6gm5i7jiHk1FBFil1xYWwl7mRVpD EkkJHM4ET5jBIn01z01COiRZsggEbLDhLyCrwWT8i+8pnHADmlmHPjMDcX3oDqL6 VRyk35BUG79ZZ5nbyMOBrV8znhTES1tb2rj1+RyAf8Yf9g4dpOvPGyXsvts4EIHu B0FZ0OJPxmYCstCcHZJc4oDZisF9OpFZyTyd -----END CERTIFICATE----- ================================================ FILE: stdlib-integration-tests/tcp/tests/tcp_tests.bal ================================================ // Copyright (c) 2021 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. // // WSO2 Inc. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except // in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. import ballerina/test; import ballerina/io; import ballerina/tcp; @test:Config {} function testClientEcho() returns @tainted error? { tcp:Client socketClient = check new ("localhost", PORT1); string msg = "Hello Ballerina Echo from client"; byte[] msgByteArray = msg.toBytes(); check socketClient->writeBytes(msgByteArray); readonly & byte[] receivedData = check socketClient->readBytes(); test:assertEquals(string:fromBytes(receivedData), msg, "Found unexpected output"); return socketClient->close(); } @test:Config { dependsOn: [testClientEcho] } function testClientReadTimeout() returns @tainted error? { tcp:Client socketClient = check new ("localhost", PORT2, timeout = 1); string msg = "Do not reply"; byte[] msgByteArray = msg.toBytes(); check socketClient->writeBytes(msgByteArray); tcp:Error|(readonly & byte[]) res = socketClient->readBytes(); if (res is (readonly & byte[])) { io:println(res.length()); test:assertFail(msg = "Read timeout test failed"); } // print expected timeout error io:println(res); return socketClient->close(); } @test:Config { dependsOn: [testClientReadTimeout] } function testServerAlreadyClosed() returns @tainted error? { tcp:Client socketClient = check new ("localhost", PORT3, timeout = 1); tcp:Error|(readonly & byte[]) res = socketClient->readBytes(); if (res is (readonly & byte[])) { io:println(res.length()); test:assertFail(msg = "Test for server already disconnected failed"); } // print expected timeout error io:println(res); return socketClient->close(); } @test:Config {dependsOn: [testServerAlreadyClosed]} function testSecureListenerWithSecureClient() returns @tainted error? { tcp:Client socketClient = check new ("localhost", PORT4, secureSocket = { cert: certPath, protocol: { name: tcp:TLS, versions: ["TLSv1.2", "TLSv1.1"] }, ciphers: ["TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"] }); string msg = "Hello Ballerina Echo from secure client"; byte[] msgByteArray = msg.toBytes(); check socketClient->writeBytes(msgByteArray); readonly & byte[] receivedData = check socketClient->readBytes(); test:assertEquals('string:fromBytes(receivedData), msg, "Found unexpected output"); return socketClient->close(); } ================================================ FILE: stdlib-integration-tests/transaction/tests/xa_transactions_test.bal ================================================ // Copyright (c) 2020-2024, WSO2 LLC. (https://www.wso2.com). // // WSO2 LLC. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except // in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. import ballerina/sql; import ballerina/test; import ballerinax/java.jdbc; string xaDatasourceName = "org.h2.jdbcx.JdbcDataSource"; @test:Config { } function testXATransactions() returns error? { string str = ""; jdbc:Client dbClient1 = check new (url = "jdbc:h2:file:./xa-transactions/testdb1", user = "test", password = "test", options = {datasourceName: xaDatasourceName} ); jdbc:Client dbClient2 = check new (url = "jdbc:h2:file:./xa-transactions/testdb2", user = "test", password = "test", options = {datasourceName: xaDatasourceName} ); _ = check dbClient1->execute(`CREATE TABLE IF NOT EXISTS EMPLOYEE (ID INT, NAME VARCHAR(30))`); _ = check dbClient2->execute(`CREATE TABLE IF NOT EXISTS SALARY (ID INT, "VALUE" FLOAT)`); transaction { str += "transaction started"; _ = check dbClient1->execute(`INSERT INTO EMPLOYEE VALUES (1, 'John')`); _ = check dbClient2->execute(`INSERT INTO SALARY VALUES (1, 20000.00)`); var commitResult = commit; if commitResult is () { str += " -> transaction committed"; } else { str += " -> transaction failed"; } str += " -> transaction ended."; } test:assertEquals(str, "transaction started -> transaction committed -> transaction ended."); // Verify that the data was inserted successfully to both databases sql:ExecutionResult employeeResult = check dbClient1->queryRow(`SELECT * FROM EMPLOYEE WHERE ID = 1`); sql:ExecutionResult salaryResult = check dbClient2->queryRow(`SELECT * FROM SALARY WHERE ID = 1`); json employeeResultJson = employeeResult.toJson(); json salaryResultJson = salaryResult.toJson(); test:assertEquals(employeeResultJson.ID, 1); test:assertEquals(employeeResultJson.NAME, "John"); test:assertEquals(salaryResultJson.ID, 1); test:assertEquals(salaryResultJson.VALUE, 20000.00); checkpanic dbClient1.close(); checkpanic dbClient2.close(); } ================================================ FILE: stdlib-integration-tests/udp/tests/mock_servers.bal ================================================ // Copyright (c) 2021 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. // // WSO2 Inc. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except // in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. import ballerina/log; import ballerina/udp; const int PORT1 = 9001; map QuestionBank = { "hi": "hi there!", "who are you?": "I'm a ballerina bot" }; service on new udp:Listener(PORT1) { remote function onDatagram(readonly & udp:Datagram datagram, udp:Caller caller ) returns udp:Datagram|udp:Error? { string|error request = string:fromBytes(datagram.data); if (request is string && QuestionBank.hasKey(request)) { udp:Datagram|error response = datagram.cloneWithType(udp:Datagram); if (response is error) { return datagram; } else { response.data = QuestionBank.get(request).toBytes(); check caller->sendDatagram(response); } } else { return datagram; } } remote function onError(udp:Error err) { log:printError("An error occurred", 'error = err); } } ================================================ FILE: stdlib-integration-tests/udp/tests/udp_tests.bal ================================================ // Copyright (c) 2021 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. // // WSO2 Inc. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except // in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. import ballerina/log; import ballerina/test; import ballerina/udp; @test:Config {} function testConnectionlessClient() returns error? { udp:Client socketClient = check new; string msg = "hi"; string expectedResponseString = "hi there!"; var sendResult = check socketClient->sendDatagram({ data: msg.toBytes(), remoteHost: "localhost", remotePort: PORT1 }); log:printInfo("Datagram was sent to the remote host."); readonly & udp:Datagram result = check socketClient->receiveDatagram(); test:assertEquals(string:fromBytes(result.data), expectedResponseString, "Found unexpected output"); check socketClient->close(); } @test:Config {} function testConnectClient() returns error? { udp:ConnectClient socketClient = check new ("localhost", PORT1); string msg = "who are you?"; string expectedResponseString = "I'm a ballerina bot"; check socketClient->writeBytes(msg.toBytes()); log:printInfo("Datagram was sent to the remote host."); readonly & byte[] result = check socketClient->readBytes(); test:assertEquals(string:fromBytes(result), expectedResponseString, "Found unexpected output"); check socketClient->close(); } @test:Config {} isolated function testConnectClientReadTimeOut() returns error? { udp:ConnectClient socketClient = check new ("localhost", 48830, localHost = "localhost", timeout = 1); var result = socketClient->readBytes(); if (result is byte[]) { test:assertFail(msg = "No UDP service running on localhost:48830, no result should be returned"); } else { log:printInfo(result.message()); } check socketClient->close(); } ================================================ FILE: stdlib-integration-tests/url/tests/url_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/url; @test:Config {} public function testUrlEncodingAndDecoding() { string input = "http://localhost:9090/echoService?type=string&value=hello world"; string output = "http%3A%2F%2Flocalhost%3A9090%2FechoService%3Ftype%3Dstring%26value%3Dhello%20world"; string|url:Error encodedResult = url:encode(input, "UTF-8"); if (encodedResult is string) { test:assertEquals(encodedResult, output); } else { test:assertFail(msg = "Test Failed!"); } string|url:Error decodedResult = url:decode(output, "UTF-8"); if (decodedResult is string) { test:assertEquals(decodedResult, input); } else { test:assertFail(msg = "Test Failed!"); } } ================================================ FILE: stdlib-integration-tests/websocket/tests/web_socket_test.bal ================================================ // Copyright (c) 2020 WSO2 Inc. (//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 // // //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/lang.runtime as runtime; import ballerina/test; import ballerina/http; import ballerina/websocket; service /onTextString on new websocket:Listener(21003) { resource isolated function get .(http:Request req) returns websocket:Service|websocket:Error { return new WsService1(); } } service class WsService1 { *websocket:Service; remote isolated function onMessage(websocket:Caller caller, string data) returns error? { check caller->writeMessage(data); } } // Tests string support for writeString and onString @test:Config {} public function testWebsocketString() returns websocket:Error? { websocket:Client wsClient = check new ("ws://localhost:21003/onTextString"); check wsClient->writeMessage("Hi"); runtime:sleep(5); string data = check wsClient->readTextMessage(); test:assertEquals(data, "Hi", msg = "Failed pushtext"); return wsClient->close(statusCode = 1000, reason = "Close the connection", timeout = 180); } ================================================ FILE: stdlib-integration-tests/websub/tests/web_sub_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/io; import ballerina/http; import ballerina/websubhub; import ballerina/test; public function startHub() returns string { io:println("Starting up the Ballerina Hub Service"); string resultString = ""; websubhub:Hub webSubHub; var result = websubhub:startHub(new http:Listener(9191), "/websub", "/hub"); if (result is websubhub:Hub) { webSubHub = result; } else if (result is websubhub:HubStartedUpError) { webSubHub = result.startedUpHub; } else { resultString = result.message(); io:println("Hub start error:" + result.message()); return resultString; } var registrationResponse = webSubHub.registerTopic("http://websubpubtopic.com"); if (registrationResponse is error) { resultString = resultString + registrationResponse.message(); io:println("Error occurred registering topic: " + registrationResponse.message()); } else { resultString = resultString + "Topic registration successful!"; io:println("Topic registration successful!"); } io:println("Publishing update to internal Hub"); var publishResponse = webSubHub.publishUpdate("http://websubpubtopic.com", {"action": "publish", "mode": "internal-hub"}); if (publishResponse is error) { resultString = resultString + publishResponse.message(); io:println("Error notifying hub: " + publishResponse.message()); } else { resultString = resultString + "Update notification successful!"; io:println("Update notification successful!"); } return resultString; } @test:Config{} public function testStartHubAndPublish() { string result = startHub(); io:println(result); test:assertEquals(result, "Topic registration successful!Update notification successful!", msg = "Error starting the hub"); } ================================================ FILE: stdlib-integration-tests/websub-advance/tests/constants.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. // Integration tests constants const string WEBSUB_PERSISTENCE_TOPIC_ONE = "http://one.persistence.topic.com"; const string WEBSUB_PERSISTENCE_TOPIC_TWO = "http://two.persistence.topic.com"; const string WEBSUB_TOPIC_ONE = "http://one.websub.topic.com"; const string ID_NOTIFICATION_ONE = "NotificationOne"; const string ID_NOTIFICATION_FOUR = "NotificationFour"; const string NOTIFICATION_ONE = "WebSub Notification Received by One: {\"mode\":\"internal\", \"content_type\":\"json\"}"; const string NOTIFICATION_FOUR = "WebSub Notification Received by Four: {\"mode\":\"remote\", \"content_type\":\"xml\"}"; ================================================ FILE: stdlib-integration-tests/websub-advance/tests/test_subscribers_at_basic_auth_secured_hub.bal ================================================ // // Copyright (c) 2019 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/auth; // import ballerina/http; // import ballerina/io; // import ballerina/lang.runtime as runtime; // import ballerina/test; // import ballerina/log; // import ballerina/websub; // auth:InboundBasicAuthProvider basicAuthProvider = new; // http:BasicAuthHandler basicAuthHandler = new(basicAuthProvider); // websub:Hub webSubHub = startHubAndRegisterTopic(); // listener http:Listener publisherServiceEP = new http:Listener(23080); // http:BasicAuthHandler outboundBasicAuthHandler = new(new auth:OutboundBasicAuthProvider({ // username: "anne", // password: "abc" // })); // websub:PublisherClient websubHubClientEP = new (webSubHub.publishUrl, { // auth: { // authHandler: outboundBasicAuthHandler // }, // secureSocket: { // trustStore: { // path: "tests/resources/security/ballerinaTruststore.p12", // password: "ballerina" // } // } // }); // http:BasicAuthHandler authnFailingHandler = new(new auth:OutboundBasicAuthProvider({ // username: "anne", // password: "cba" // })); // websub:PublisherClient authnFailingClient = new (webSubHub.publishUrl, { // auth: { // authHandler: authnFailingHandler // }, // secureSocket: { // trustStore: { // path: "tests/resources/security/ballerinaTruststore.p12", // password: "ballerina" // } // } // }); // http:BasicAuthHandler authzFailingHandler = new(new auth:OutboundBasicAuthProvider({ // username: "peter", // password: "pqr" // })); // websub:PublisherClient authzFailingClient = new (webSubHub.publishUrl, { // auth: { // authHandler: authzFailingHandler // }, // secureSocket: { // trustStore: { // path: "tests/resources/security/ballerinaTruststore.p12", // password: "ballerina" // } // } // }); // service publisher on publisherServiceEP { // @http:ResourceConfig { // methods: ["GET", "HEAD"] // } // resource function discover(http:Caller caller, http:Request req) { // http:Response response = new; // // Add a link header indicating the hub and topic // websub:addWebSubLinkHeader(response, [webSubHub.subscriptionUrl], WEBSUB_PERSISTENCE_TOPIC_ONE); // var err = caller->accepted(response); // if (err is error) { // log:printError("Error responding on discovery", err = err); // } // } // @http:ResourceConfig { // methods: ["POST"], // path: "/notify/{subscriber}" // } // resource function notify(http:Caller caller, http:Request req, string subscriber) { // var payload = req.getJsonPayload(); // if (payload is error) { // panic payload; // } // checkSubscriberAvailability(WEBSUB_PERSISTENCE_TOPIC_ONE, "http://localhost:" + subscriber + "/websub"); // var err = webSubHub.publishUpdate(WEBSUB_PERSISTENCE_TOPIC_ONE, <@untainted> payload); // if (err is error) { // log:printError("Error publishing update directly", err = err); // } // http:Response response = new; // err = caller->accepted(response); // if (err is error) { // log:printError("Error responding on notify request", err = err); // } // } // } // service publisherTwo on publisherServiceEP { // @http:ResourceConfig { // methods: ["GET", "HEAD"] // } // resource function discover(http:Caller caller, http:Request req) { // http:Response response = new; // // Add a link header indicating the hub and topic // websub:addWebSubLinkHeader(response, [webSubHub.subscriptionUrl], WEBSUB_PERSISTENCE_TOPIC_TWO); // var err = caller->accepted(response); // if (err is error) { // log:printError("Error responding on discovery", err = err); // } // } // @http:ResourceConfig { // methods: ["POST"] // } // resource function notify(http:Caller caller, http:Request req) { // var payload = req.getJsonPayload(); // if (payload is error) { // panic payload; // } // checkSubscriberAvailability(WEBSUB_PERSISTENCE_TOPIC_TWO, "http://localhost:23383/websubTwo"); // var err = webSubHub.publishUpdate(WEBSUB_PERSISTENCE_TOPIC_TWO, <@untainted> payload); // if (err is error) { // log:printError("Error publishing update directly", err = err); // } // http:Response response = new; // err = caller->accepted(response); // if (err is error) { // log:printError("Error responding on notify request", err = err); // } // } // } // service publisherThree on publisherServiceEP { // @http:ResourceConfig { // methods: ["GET", "HEAD"] // } // resource function discover(http:Caller caller, http:Request req) { // http:Response response = new; // // Add a link header indicating the hub and topic // websub:addWebSubLinkHeader(response, [webSubHub.subscriptionUrl], WEBSUB_TOPIC_ONE); // var err = caller->accepted(response); // if (err is error) { // log:printError("Error responding on discovery", err = err); // } // } // @http:ResourceConfig { // methods: ["POST"] // } // resource function notify(http:Caller caller, http:Request req) { // var payload = req.getJsonPayload(); // if (payload is error) { // panic payload; // } // checkSubscriberAvailability(WEBSUB_TOPIC_ONE, "http://localhost:23484/websubFour"); // string publishErrorMessagesConcatenated = ""; // var err = websubHubClientEP->publishUpdate(WEBSUB_TOPIC_ONE, <@untainted> payload); // if (err is error) { // publishErrorMessagesConcatenated += err.message(); // log:printError("Error publishing update remotely", err = err); // } // err = authnFailingClient->publishUpdate(WEBSUB_TOPIC_ONE, <@untainted> payload); // if (err is error) { // publishErrorMessagesConcatenated += err.message(); // log:printError("Error publishing update remotely", err = err); // } // err = authzFailingClient->publishUpdate(WEBSUB_TOPIC_ONE, <@untainted> payload); // if (err is error) { // publishErrorMessagesConcatenated += err.message(); // log:printError("Error publishing update remotely", err = err); // } // err = caller->accepted(<@untainted> publishErrorMessagesConcatenated); // if (err is error) { // log:printError("Error responding on notify request", err = err); // } // } // } // service helperService on publisherServiceEP { // @http:ResourceConfig { // methods: ["POST"] // } // resource function restartHub(http:Caller caller, http:Request req) { // checkpanic webSubHub.stop(); // webSubHub = startHubAndRegisterTopic(); // checkpanic caller->accepted(); // } // } // function startHubAndRegisterTopic() returns websub:Hub { // websub:Hub internalHub = startWebSubHub(); // var err = internalHub.registerTopic(WEBSUB_PERSISTENCE_TOPIC_ONE); // if (err is error) { // log:printError("Error registering topic", err = err); // } // err = internalHub.registerTopic(WEBSUB_PERSISTENCE_TOPIC_TWO); // if (err is error) { // log:printError("Error registering topic", err = err); // } // err = internalHub.registerTopic(WEBSUB_TOPIC_ONE); // if (err is error) { // log:printError("Error registering topic", err = err); // } // return internalHub; // } // function startWebSubHub() returns websub:Hub { // var result = websub:startHub(new http:Listener(23192, config = { // auth: { // authHandlers: [basicAuthHandler] // }, // secureSocket: { // keyStore: { // path: "tests/resources/security/ballerinaKeystore.p12", // password: "ballerina" // }, // trustStore: { // path: "tests/resources/security/ballerinaTruststore.p12", // password: "ballerina" // } // } // }), "/websub", "/hub", // serviceAuth = {enabled:true}, // subscriptionResourceAuth = {enabled:true, scopes:["subscribe"]}, // publisherResourceAuth = {enabled:true, scopes:["publish"]}, // hubConfiguration = { remotePublish : { enabled : true }} // ); // if (result is websub:Hub) { // return result; // } else if (result is websub:HubStartedUpError) { // return result.startedUpHub; // } else { // panic result; // } // } // function checkSubscriberAvailability(string topic, string callback) { // int count = 0; // boolean subscriberAvailable = false; // while (!subscriberAvailable && count < 60) { // websub:SubscriberDetails[] topicDetails = webSubHub.getSubscribers(topic); // if (isSubscriberAvailable(topicDetails, callback)) { // return; // } // runtime:sleep(1); // count += 1; // } // } // function isSubscriberAvailable(websub:SubscriberDetails[] topicDetails, string callback) returns boolean { // foreach var detail in topicDetails { // if (detail.callback == callback) { // return true; // } // } // return false; // } // listener websub:Listener websubEP = new websub:Listener(23484); // auth:OutboundBasicAuthProvider basicAuthProvider1 = new({ // username: "peter", // password: "pqr" // }); // http:BasicAuthHandler basicAuthHandler1 = new(basicAuthProvider1); // @websub:SubscriberServiceConfig { // path: "/websub", // subscribeOnStartUp: true, // target: "http://localhost:23080/publisher/discover", // leaseSeconds: 3600, // secret: "Kslk30SNF2AChs2", // hubClientConfig: { // auth: { authHandler: basicAuthHandler1 }, // secureSocket: { // trustStore: { // path: "tests/resources/security/ballerinaTruststore.p12", // password: "ballerina" // } // } // } // } // service websubSubscriber on websubEP { // resource function onNotification (websub:Notification notification) returns @tainted error? { // json payload = check notification.getJsonPayload(); // io:println("WebSub Notification Received by One: " + <@untainted>payload.toJsonString()); // storeOutput(ID_NOTIFICATION_ONE, "WebSub Notification Received by One: " + <@untainted>payload.toJsonString()); // } // } // auth:OutboundBasicAuthProvider basicAuthProvider2 = new({ // username: "tom", // password: "4321" // }); // http:BasicAuthHandler basicAuthHandler2 = new(basicAuthProvider2); // @websub:SubscriberServiceConfig { // path: "/websubTwo", // subscribeOnStartUp: true, // target: "http://localhost:23080/publisherTwo/discover", // leaseSeconds: 1200, // hubClientConfig: { // auth: { authHandler: basicAuthHandler2 }, // secureSocket: { // trustStore: { // path: "tests/resources/security/ballerinaTruststore.p12", // password: "ballerina" // } // } // } // } // service websubSubscriberTwo on websubEP { // resource function onNotification (websub:Notification notification) returns @tainted error? { // json payload = check notification.getJsonPayload(); // io:println("WebSub Notification Received by Two: " + payload.toJsonString()); // } // } // auth:OutboundBasicAuthProvider basicAuthProvider3 = new({ // username: "mary", // password: "xyz" // }); // http:BasicAuthHandler basicAuthHandler3 = new(basicAuthProvider3); // @websub:SubscriberServiceConfig { // path: "/websubThree", // subscribeOnStartUp: true, // target: "http://localhost:23080/publisher/discover", // leaseSeconds: 1200, // hubClientConfig: { // auth: { authHandler: basicAuthHandler3 }, // secureSocket: { // trustStore: { // path: "tests/resources/security/ballerinaTruststore.p12", // password: "ballerina" // } // } // } // } // service websubSubscriberThree on websubEP { // resource function onNotification (websub:Notification notification) returns @tainted error? { // json payload = check notification.getJsonPayload(); // io:println("WebSub Notification Received by Three: " + payload.toJsonString()); // } // } // auth:OutboundBasicAuthProvider basicAuthProvider4 = new({ // username: "tom", // password: "1234" // }); // http:BasicAuthHandler basicAuthHandler4 = new(basicAuthProvider4); // @websub:SubscriberServiceConfig { // path: "/websubFour", // target: "http://localhost:23080/publisherThree/discover", // leaseSeconds: 1200, // hubClientConfig: { // auth: { authHandler: basicAuthHandler4 }, // secureSocket: { // trustStore: { // path: "tests/resources/security/ballerinaTruststore.p12", // password: "ballerina" // } // } // } // } // service websubSubscriberFour on websubEP { // resource function onNotification (websub:Notification notification) returns @tainted error? { // json payload = check notification.getJsonPayload(); // io:println("WebSub Notification Received by Four: " + <@untainted>payload.toJsonString()); // storeOutput(ID_NOTIFICATION_FOUR, "WebSub Notification Received by Four: " + <@untainted>payload.toJsonString()); // } // } // @test:Config{} // function testDiscoveryAndIntentVerification() { // http:Client clientEndpoint = new ("http://localhost:23080"); // json jsonPayload = {mode: "internal", content_type: "json"}; // http:Request req = new; // req.addHeader(http:CONTENT_TYPE, "application/json"); // req.setJsonPayload(jsonPayload); // var response = clientEndpoint->post("/publisher/notify/23484", req); // runtime:sleep(10); // test:assertEquals(fetchOutput(ID_NOTIFICATION_ONE), NOTIFICATION_ONE, msg = "Response code mismatched"); // } // @test:Config{ // dependsOn: [testDiscoveryAndIntentVerification] // } // function testContentReceipt() { // http:Client clientEndpoint = new ("http://localhost:23080"); // json jsonPayload = {mode: "remote", content_type: "xml"}; // http:Request req = new; // req.addHeader(http:CONTENT_TYPE, "application/json"); // req.setJsonPayload(jsonPayload); // var response = clientEndpoint->post("/publisherThree/notify", req); // runtime:sleep(10); // test:assertEquals(fetchOutput(ID_NOTIFICATION_FOUR), NOTIFICATION_FOUR, msg = "Response code mismatched"); // } ================================================ FILE: stdlib-integration-tests/websub-advance/tests/test_subscribers_at_persistence_enabled_hub.bal ================================================ //// Copyright (c) 2019 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/auth; //import ballerina/io; //import ballerina/http; // //listener Listener persistenceWebsubEP = new Listener(23383); // //auth:OutboundBasicAuthProvider persistenceBasicAuthProvider = new({ // username: "tom", // password: "1234" //}); // //http:BasicAuthHandler persistenceBasicAuthHandler = new(persistenceBasicAuthProvider); // //@SubscriberServiceConfig { // path: "/websub", // target: "http://localhost:23080/publisher/discover", // leaseSeconds: 3600, // secret: "Kslk30SNF2AChs2", // hubClientConfig: { // auth: { authHandler: persistenceBasicAuthHandler } // } //} //service persistenceWebsubSubscriber on persistenceWebsubEP { // resource function onNotification (Notification notification) returns @tainted error? { // json payload = check notification.getJsonPayload(); // io:println("WebSub Notification Received by One: " + payload.toJsonString()); // } //} // //@SubscriberServiceConfig { // path: "/websubTwo", // subscribeOnStartUp: true, // target: "http://localhost:23080/publisherTwo/discover", // leaseSeconds: 1200, // hubClientConfig: { // auth: { authHandler: persistenceBasicAuthHandler } // } //} //service persistenceWebsubSubscriberTwo on persistenceWebsubEP { // resource function onNotification (Notification notification) returns @tainted error? { // json payload = check notification.getJsonPayload(); // io:println("WebSub Notification Received by Two: " + payload.toJsonString()); // } //} ================================================ FILE: stdlib-integration-tests/websub-advance/tests/utils.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. map<(anydata|error)> outputs = {}; public function storeOutput(string key, any|error... s) { any|error statusCode = s[0]; outputs[key] = statusCode is error ? statusCode.toString() : statusCode.toString(); } public function fetchOutput(string key) returns (anydata|error) { return outputs[key]; } ================================================ FILE: stdlib-integration-tests/websub-generic/tests/01_websub_publisher.bal ================================================ // Copyright (c) 2018 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. // // WSO2 Inc. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except // in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. import ballerina/http; import ballerina/io; import ballerina/log; import ballerina/lang.runtime as runtime; import ballerina/websubhub; boolean remoteTopicRegistered = false; websubhub:Hub webSubHub = startHubAndRegisterTopic(); websubhub:PublisherClient websubHubClientEP = new (webSubHub.publishUrl); listener http:Listener publisherServiceEP = new http:Listener(23080); service /publisher on publisherServiceEP { resource function get discover(http:Caller caller, http:Request req) { http:Response response = new; // Add a link header indicating the hub and topic websubhub:addWebSubLinkHeader(response, [webSubHub.subscriptionUrl], WEBSUB_TOPIC_ONE); var err = caller->accepted(response); if (err is error) { log:printError("Error responding on ordering", err = err); } } resource function post notify/[string subscriber](http:Caller caller, http:Request req) { remoteRegisterTopic(); json jsonPayload = checkpanic req.getJsonPayload(); json jsonMode = checkpanic jsonPayload.mode; string mode = jsonMode.toJsonString(); json jsonContentType = checkpanic jsonPayload.content_type; string contentType = jsonContentType.toJsonString(); var err = caller->accepted(); if (err is error) { log:printError("Error responding on notify request", err = err); } if (subscriber != "skip_subscriber_check") { checkSubscriberAvailability(WEBSUB_TOPIC_ONE, "http://localhost:" + subscriber + "/websub"); checkSubscriberAvailability(WEBSUB_TOPIC_ONE, "http://localhost:" + subscriber + "/subscriberWithNoPathInAnnot"); checkSubscriberAvailability(WEBSUB_TOPIC_ONE, "http://localhost:" + subscriber + "/websubThree?topic=" + WEBSUB_TOPIC_ONE + "&fooVal=barVal"); } if (mode == "internal") { var result = webSubHub.publishUpdate(WEBSUB_TOPIC_ONE, getPayloadContent(contentType, mode)); if (result is error) { log:printError("Error publishing update directly", err = result); } } else { var result = websubHubClientEP->publishUpdate(WEBSUB_TOPIC_ONE, getPayloadContent(contentType, mode)); if (result is error) { log:printError("Error publishing update remotely", err = result); } } } resource function get topicInfo(http:Caller caller, http:Request req) { if (req.hasHeader("x-topic")) { string topicName = req.getHeader("x-topic"); websubhub:SubscriberDetails[] details = webSubHub.getSubscribers(topicName); var err = caller->respond(details.toString()); if (err is error) { log:printError("Error responding on topicInfo request", err = err); } } else { map allTopics = {}; int index=1; string [] availableTopics = webSubHub.getAvailableTopics(); foreach var topic in availableTopics { allTopics["Topic_" + index.toString()] = topic; index += 1; } json j = checkpanic allTopics.cloneWithType(JsonTypedesc); var err = caller->respond(j); if (err is error) { log:printError("Error responding on topicInfo request", err = err); } } } resource function get unsubscribe(http:Caller caller, http:Request req) returns error? { check webSubHub.removeSubscription("http://one.websub.topic.com", "http://localhost:23181/websubThree?topic=http://one.websub.topic.com&fooVal=barVal"); var err = caller->respond("unsubscription successful"); if (err is error) { log:printError("Error responding on unsubscription request", err = err); } } } service /publisherTwo on publisherServiceEP { resource function get discover(http:Caller caller, http:Request req) { http:Response response = new; // Add a link header indicating the hub and topic websubhub:addWebSubLinkHeader(response, [webSubHub.subscriptionUrl], WEBSUB_TOPIC_FOUR); var err = caller->accepted(response); if (err is error) { log:printError("Error responding on ordering", err = err); } } resource function post notify(http:Caller caller, http:Request req) { checkSubscrberAvailabilityAndPublishDirectly(WEBSUB_TOPIC_THREE, "http://localhost:23383/websub", {"action":"publish","mode":"internal-hub"}); checkSubscrberAvailabilityAndPublishDirectly(WEBSUB_TOPIC_FOUR, "http://localhost:23383/websubTwo", {"action":"publish","mode":"internal-hub-two"}); var err = caller->accepted(); if (err is error) { log:printError("Error responding on notify request", err = err); } } } service /contentTypePublisher on publisherServiceEP { resource function post notify/[string port](http:Caller caller, http:Request req) { json jsonPayload = checkpanic req.getJsonPayload(); json jsonMode = checkpanic jsonPayload.mode; string mode = jsonMode.toJsonString(); json jsonContentType = checkpanic jsonPayload.content_type; string contentType = jsonContentType.toJsonString(); var err = caller->accepted(); if (err is error) { log:printError("Error responding on notify request", err = err); } if (port != "skip_subscriber_check") { checkSubscriberAvailability(WEBSUB_TOPIC_ONE, "http://localhost:" + port + "/websub"); checkSubscriberAvailability(WEBSUB_TOPIC_ONE, "http://localhost:" + port + "/websubTwo"); } if (mode == "internal") { var result = webSubHub.publishUpdate(WEBSUB_TOPIC_ONE, getPayloadContent(contentType, mode)); if (result is error) { log:printError("Error publishing update directly", err = result); } } else { var result = websubHubClientEP->publishUpdate(WEBSUB_TOPIC_ONE, getPayloadContent(contentType, mode)); if (result is error) { log:printError("Error publishing update remotely", err = result); } } } } function checkSubscrberAvailabilityAndPublishDirectly(string topic, string subscriber, json payload) { checkSubscriberAvailability(topic, subscriber); var err = webSubHub.publishUpdate(topic, payload); if (err is error) { log:printError("Error publishing update directly", err = err); } } function startHubAndRegisterTopic() returns websubhub:Hub { websubhub:Hub internalHub = startWebSubHub(); var err = internalHub.registerTopic(WEBSUB_TOPIC_ONE); if (err is error) { log:printError("Error registering topic directly", err = err); } err = internalHub.registerTopic(WEBSUB_TOPIC_THREE); if (err is error) { log:printError("Error registering topic directly", err = err); } err = internalHub.registerTopic(WEBSUB_TOPIC_FOUR); if (err is error) { log:printError("Error registering topic directly", err = err); } err = internalHub.registerTopic(WEBSUB_TOPIC_FIVE); if (err is error) { log:printError("Error registering topic directly", err = err); } err = internalHub.registerTopic(WEBSUB_TOPIC_SIX); if (err is error) { log:printError("Error registering topic directly", err = err); } return internalHub; } function startWebSubHub() returns websubhub:Hub { var result = websubhub:startHub(new http:Listener(23191), "/websub", "/hub", hubConfiguration = { remotePublish : { enabled : true }}); if (result is websubhub:Hub) { return result; } else if (result is websubhub:HubStartedUpError) { return result.startedUpHub; } else { panic result; } } function remoteRegisterTopic() { if (remoteTopicRegistered) { return; } var err = websubHubClientEP->registerTopic(WEBSUB_TOPIC_TWO); if (err is error) { log:printError("Error registering topic remotely", err = err); } remoteTopicRegistered = true; } function getPayloadContent(string contentType, string mode) returns string|xml|json|byte[]|io:ReadableByteChannel { string errorMessage = "unknown content type"; if (contentType == "" || contentType == "json") { if (mode == "internal") { json j = {"action":"publish","mode":"internal-hub"}; return j; } json k = {"action":"publish","mode":"remote-hub"}; return k; } else if (contentType == "string") { if (mode == "internal") { return "Text update for internal Hub"; } return "Text update for remote Hub"; } else if (contentType == "xml") { if (mode == "internal") { return xml `NotificationInternal`; } return xml `NotificationRemote`; } else if (contentType == "byte[]" || contentType == "io:ReadableByteChannel") { errorMessage = "content type " + contentType + " not yet supported with WebSub tests"; } error e = error(errorMessage); panic e; } function checkSubscriberAvailability(string topic, string callback) { int count = 0; boolean subscriberAvailable = false; while (!subscriberAvailable && count < 60) { websubhub:SubscriberDetails[] topicDetails = webSubHub.getSubscribers(topic); if (isSubscriberAvailable(topicDetails, callback)) { return; } runtime:sleep(1); count += 1; } } function isSubscriberAvailable(websubhub:SubscriberDetails[] topicDetails, string callback) returns boolean { foreach var detail in topicDetails { if (detail.callback == callback) { return true; } } return false; } type JsonTypedesc typedesc; ================================================ FILE: stdlib-integration-tests/websub-generic/tests/02_redirection_publishers.bal ================================================ // Copyright (c) 2018 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. // // WSO2 Inc. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except // in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. import ballerina/http; import ballerina/log; import ballerina/websubhub; listener http:Listener publisherServiceEPTwo = new http:Listener(23081); service /original on publisherServiceEPTwo { resource function get one(http:Caller caller, http:Request req) { http:Response res = new; checkpanic caller->redirect(res, http:REDIRECT_MOVED_PERMANENTLY_301, ["http://localhost:23081/redirected/one"]); } resource function get two(http:Caller caller, http:Request req) { http:Response res = new; checkpanic caller->redirect(res, http:REDIRECT_FOUND_302, ["http://localhost:23081/redirected/two"]); } } service /redirected on publisherServiceEPTwo { resource function get one(http:Caller caller, http:Request req) { http:Response res = new; websubhub:addWebSubLinkHeader(res, ["http://localhost:23081/hub/one"], WEBSUB_TOPIC_FIVE); var err = caller->respond(res); if (err is error) { log:printError("Error sending response", err = err); } else { storeOutput(ID_REDIRECT_SUBSCRIBER_ONE_LOG, REDIRECT_SUBSCRIBER_ONE_LOG); } } resource function get two(http:Caller caller, http:Request req) { http:Response res = new; websubhub:addWebSubLinkHeader(res, ["http://localhost:23081/hub/two"], WEBSUB_TOPIC_SIX); var err = caller->respond(res); if (err is error) { log:printError("Error sending response", err = err); } else { storeOutput(ID_REDIRECT_SUBSCRIBER_TWO_LOG, REDIRECT_SUBSCRIBER_TWO_LOG); } } } service /hub on publisherServiceEPTwo { resource function get one(http:Caller caller, http:Request req) { http:Response res = new; checkpanic caller->redirect(res, http:REDIRECT_TEMPORARY_REDIRECT_307, ["http://localhost:23191/websub/hub"]); } resource function get two(http:Caller caller, http:Request req) { http:Response res = new; checkpanic caller->redirect(res, http:REDIRECT_PERMANENT_REDIRECT_308, ["http://localhost:23191/websub/hub"]); } } ================================================ FILE: stdlib-integration-tests/websub-generic/tests/constants.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. // Integration tests constants const string CUSTOM_SUB_MOCK_HEADER = "MockHeader"; const string MOCK_HEADER = "MockHeader"; const string CONTENT_TYPE_JSON = "application/json"; const string CONTENT_TYPE_FORM_URL_ENCODED = "application/x-www-form-urlencoded"; const string HUB_MODE_INTERNAL = "internal"; const string HUB_MODE_REMOTE = "remote"; const string TYPE_JSON = "json"; const string TYPE_XML = "xml"; const string TYPE_STRING = "string"; const string SKIP_SUBSCRIBER_CHECK = "skip_subscriber_check"; const string COMMON_PATH = "/contentTypePublisher/notify/"; const string WEBSUB_TOPIC_ONE = "http://one.websub.topic.com"; const string WEBSUB_TOPIC_TWO = "http://two.websub.topic.com"; const string WEBSUB_TOPIC_THREE = "http://three.websub.topic.com"; const string WEBSUB_TOPIC_FOUR = "http://four.websub.topic.com"; const string WEBSUB_TOPIC_FIVE = "http://one.redir.topic.com"; const string WEBSUB_TOPIC_SIX = "http://two.redir.topic.com"; const string HEADER_ACCEPT = "Accept"; const string HEADER_ACCEPT_LANGUAGE = "Accept-Language"; const string ID_MATCH_ACCEPT_AND_ACCEPT_LANGUAGE_HEADER_ARRAY = "MatchingAcceptAndAcceptLanguageHeaderArray"; const string ID_MATCH_ACCEPT_AND_ACCEPT_LANGUAGE_HEADERS = "MatchingAcceptAndAcceptLanguageHeaders"; const string ID_MATCH_ACCEPT_HEADER = "MatchingAcceptHeader"; const string ID_MATCH_ACCEPT_LANGUAGE_HEADER = "MatchingAcceptLanguageHeader"; const string ID_MISMATCH_ACCEPT_AND_ACCEPT_LANGUAGE_HEADER_ARRAY = "MisMatchingAcceptAcceptLanguageHeaderArray"; const string ID_MISMATCH_ACCEPT_AND_ACCEPT_LANGUAGE_HEADERS = "MisMatchingAcceptAndAcceptLanguageHeaders"; const string ID_MISMATCH_ACCEPT_HEADER = "MisMathingAcceptHeader"; const string ID_MISMATCH_ACCEPT_LANGUAGE_HEADER = "MisMatchingAcceptLanguageHeader"; const string ID_MISSING_ACCEPT_AND_ACCEPT_LANGUAGE_HEADERS = "MissingAcceptAndAcceptLanguageHeaders"; const string LANGUAGE_TYPE_DE = "de-DE"; const string RESPONSE_CODE_ACCEPTED = "202"; const string RESPONSE_CODE_NOT_ACCEPTABLE = "406"; const string RESPONSE_CODE_INTERNAL_SERVER_ERROR = "500"; const string ID_INTENT_VER_REQ_RECEIVED_LOG = "IntentVerificationInvocation"; const string ID_BY_KEY_CREATED_LOG = "DispatchingByKeyCreated"; const string ID_BY_KEY_FEATURE_LOG = "DispatchingByKeyFeatured"; const string ID_BY_HEADER_ISSUE_LOG = "DispatchingByHeaderIssue"; const string ID_BY_HEADER_COMMIT_LOG = "DispatchingByHeaderCommit"; const string ID_BY_HEADER_AND_PAYLOAD_ISSUE_CREATED_LOG = "DispatchingByHeaderAndPayloadKeyCreated"; const string ID_BY_HEADER_AND_PAYLOAD_FEATURE_PULL_LOG = "DispatchingByHeaderAndPayloadKeyFeature"; const string ID_BY_HEADER_AND_PAYLOAD_HEADER_ONLY_LOG = "DispatchingByHeaderAndPayloadKeyForOnlyHeader"; const string ID_BY_HEADER_AND_PAYLOAD_KEY_ONLY_LOG = "DispatchingByHeaderAndPayloadKeyForOnlyKey"; const string ID_REDIRECT_SUBSCRIBER_ONE_LOG = "RedirectSubscriberOneLog"; const string ID_REDIRECT_SUBSCRIBER_TWO_LOG = "RedirectSubscriberTwoLog"; const string ID_INTERNAL_HUB_NOTIFICATION_SUBSCRIBER_ONE_LOG = "InternalHubNotificationSubscriberOne"; const string ID_INTERNAL_HUB_NOTIFICATION_SUBSCRIBER_TWO_LOG = "InternalHubNotificationSubscriberTwo"; const string ID_EXPLICIT_INTENT_VERIFICATION_LOG = "SubscriptionAndExplicitIntentVerification"; const string ID_HUB_NOTIFICATION_LOG = "InternalAndRemoteHubNotification"; const string ID_HUB_NOTIFICATION_LOG_TWO = "InternalAndRemoteHubNotificationTwo"; const string ID_QUERY_PARAM_LOG = "ContentReceiptForCallbackWithQueryParams"; const string ID_TEXT_SUBSCRIBER_ONE = "ContentTypeSubscriberOneText"; const string ID_XML_SUBSCRIBER_ONE = "ContentTypeSubscriberOneXml"; const string ID_JSON_SUBSCRIBER_ONE = "ContentTypeSubscriberOneJson"; const string ID_TEXT_SUBSCRIBER_TWO = "ContentTypeSubscriberTwoText"; const string ID_XML_SUBSCRIBER_TWO = "ContentTypeSubscriberTwoXml"; const string ID_JSON_SUBSCRIBER_TWO = "ContentTypeSubscriberTwoJson"; const string INTENT_VER_REQ_RECEIVED_LOG = "Intent verification request received"; const string BY_KEY_CREATED_LOG = "Created Notification Received, action: created"; const string BY_KEY_FEATURE_LOG = "Feature Notification Received, domain: feature"; const string BY_HEADER_ISSUE_LOG = "Issue Notification Received, header value: issue action: deleted"; const string BY_HEADER_COMMIT_LOG = "Commit Notification Received, header value: commit action: created"; const string BY_HEADER_AND_PAYLOAD_ISSUE_CREATED_LOG = "Issue Created Notification Received, header value: issue action: created"; const string BY_HEADER_AND_PAYLOAD_FEATURE_PULL_LOG = "Feature Pull Notification Received, header value: pull domain: feature"; const string BY_HEADER_AND_PAYLOAD_HEADER_ONLY_LOG = "HeaderOnly Notification Received, header value: headeronly action: header_only"; const string BY_HEADER_AND_PAYLOAD_KEY_ONLY_LOG = "KeyOnly Notification Received, header value: key_only action: keyonly"; const string REDIRECT_SUBSCRIBER_ONE_LOG = "Successful redirect subscriber one"; const string REDIRECT_SUBSCRIBER_TWO_LOG = "Successful redirect subscriber two"; const string INTERNAL_HUB_NOTIFICATION_SUBSCRIBER_ONE_LOG = "WebSub Notification Received by One: {\"action\":\"publish\", \"mode\":\"internal-hub\"}"; const string INTERNAL_HUB_NOTIFICATION_SUBSCRIBER_TWO_LOG = "WebSub Notification Received by Two: {\"action\":\"publish\", \"mode\":\"internal-hub-two\"}"; const string EXPLICIT_INTENT_VERIFICATION_LOG = "Intent verified explicitly for subscription change request"; const string INTERNAL_HUB_NOTIFICATION_LOG = "WebSub Notification Received: {\"action\":\"publish\", \"mode\":\"internal-hub\"}"; const string REMOTE_HUB_NOTIFICATION_LOG = "WebSub Notification Received: {\"action\":\"publish\", \"mode\":\"remote-hub\"}"; const string INTERNAL_HUB_NOTIFICATION_LOG_TWO = "WebSub Notification Received by Two: {\"action\":\"publish\", \"mode\":\"internal-hub\"}"; const string REMOTE_HUB_NOTIFICATION_LOG_TWO = "WebSub Notification Received by Two: {\"action\":\"publish\", \"mode\":\"remote-hub\"}"; const string QUERY_PARAM_LOG = "Query Params: {\"fooVal\":[\"barVal\"],\"topic\":[\"http://one.websub.topic.com\"]}"; const string XML_INTERNAL_HUB_NOTIFICATION_SUBSCRIBER_ONE_LOG = "XML WebSub Notification Received by websubSubscriber: NotificationInternal"; const string TEXT_INTERNAL_HUB_NOTIFICATION_SUBSCRIBER_ONE_LOG = "Text WebSub Notification Received by websubSubscriber: Text update for internal Hub"; const string JSON_INTERNAL_HUB_NOTIFICATION_SUBSCRIBER_ONE_LOG = "JSON WebSub Notification Received by websubSubscriber: {\"action\":\"publish\", \"mode\":\"internal-hub\"}"; const string XML_INTERNAL_HUB_NOTIFICATION_SUBSCRIBER_TWO_LOG = "XML WebSub Notification Received by websubSubscriberTwo: NotificationInternal"; const string TEXT_INTERNAL_HUB_NOTIFICATION_SUBSCRIBER_TWO_LOG = "Text WebSub Notification Received by websubSubscriberTwo: Text update for internal Hub"; const string JSON_INTERNAL_HUB_NOTIFICATION_SUBSCRIBER_TWO_LOG = "JSON WebSub Notification Received by websubSubscriberTwo: {\"action\":\"publish\", \"mode\":\"internal-hub\"}"; const string XML_REMOTE_HUB_NOTIFICATION_SUBSCRIBER_ONE_LOG = "XML WebSub Notification Received by websubSubscriber: NotificationRemote"; const string TEXT_REMOTE_HUB_NOTIFICATION_SUBSCRIBER_ONE_LOG = "Text WebSub Notification Received by websubSubscriber: Text update for remote Hub"; const string JSON_REMOTE_HUB_NOTIFICATION_SUBSCRIBER_ONE_LOG = "JSON WebSub Notification Received by websubSubscriber: {\"action\":\"publish\", \"mode\":\"remote-hub\"}"; const string XML_REMOTE_HUB_NOTIFICATION_SUBSCRIBER_TWO_LOG = "XML WebSub Notification Received by websubSubscriberTwo: NotificationRemote"; const string TEXT_REMOTE_HUB_NOTIFICATION_SUBSCRIBER_TWO_LOG = "Text WebSub Notification Received by websubSubscriberTwo: Text update for remote Hub"; const string JSON_REMOTE_HUB_NOTIFICATION_SUBSCRIBER_TWO_LOG = "JSON WebSub Notification Received by websubSubscriberTwo: {\"action\":\"publish\", \"mode\":\"remote-hub\"}"; const string WEBSUB_PERSISTENCE_TOPIC_ONE = "http://one.persistence.topic.com"; const string WEBSUB_PERSISTENCE_TOPIC_TWO = "http://two.persistence.topic.com"; const string ID_NOTIFICATION_ONE = "NotificationOne"; const string ID_NOTIFICATION_FOUR = "NotificationFour"; const string NOTIFICATION_ONE = "WebSub Notification Received by One: {\"mode\":\"internal\", \"content_type\":\"json\"}"; const string NOTIFICATION_FOUR = "WebSub Notification Received by Four: {\"mode\":\"remote\", \"content_type\":\"xml\"}"; ================================================ FILE: stdlib-integration-tests/websub-generic/tests/test_content_negotiation.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/log; import ballerina/stringutils; import ballerina/test; import ballerina/websub; import ballerina/websubhub; websubhub:Hub WebSubHub = startHubAndRegisterTopic(); listener http:Listener publisherServiceEPOne = new http:Listener(24080); listener websub:Listener websubSubscriberEPOne = new websub:Listener(24081); listener websub:Listener websubSubscriberEPTwo = new websub:Listener(24082); listener websub:Listener websubSubscriberEPThree = new websub:Listener(24083); listener websub:Listener websubSubscriberEPFour = new websub:Listener(24084); listener websub:Listener websubSubscriberEPFive = new websub:Listener(24085); listener websub:Listener websubSubscriberEPSix = new websub:Listener(24086); listener websub:Listener websubSubscriberEPSeven = new websub:Listener(24087); listener websub:Listener websubSubscriberEPEight = new websub:Listener(24088); listener websub:Listener websubSubscriberEPNine = new websub:Listener(24089); service /publisherService on publisherServiceEPOne { resource function get withAcceptAndAcceptLanguageHeaderArray(http:Caller caller, http:Request req) { http:Response response = new; if (!(req.hasHeader(HEADER_ACCEPT) && req.hasHeader(HEADER_ACCEPT_LANGUAGE))) { response.statusCode = 500; var result = caller->respond(response); log:printError("Error responding", err = result); storeOutput(ID_MISSING_ACCEPT_AND_ACCEPT_LANGUAGE_HEADERS, response.statusCode); } else { string mediaType = req.getHeader(HEADER_ACCEPT); string languageType = req.getHeader(HEADER_ACCEPT_LANGUAGE); if (stringutils:contains(mediaType, CONTENT_TYPE_JSON) && stringutils:contains(languageType, LANGUAGE_TYPE_DE)) { websubhub:addWebSubLinkHeader(response, [WebSubHub.subscriptionUrl], WEBSUB_TOPIC_ONE); response.statusCode = 202; var result = caller->respond(response); storeOutput(ID_MATCH_ACCEPT_AND_ACCEPT_LANGUAGE_HEADER_ARRAY, response.statusCode); if (result is error) { log:printError("Error responding", err = result); } } else { response.statusCode = 406; var result = caller->respond(response); log:printError("Error responding", err = result); storeOutput(ID_MISMATCH_ACCEPT_AND_ACCEPT_LANGUAGE_HEADER_ARRAY, response.statusCode); } } } resource function get withAcceptAndAcceptLanguageHeaders(http:Caller caller, http:Request req) { http:Response response = new; if (!(req.hasHeader(HEADER_ACCEPT) && req.hasHeader(HEADER_ACCEPT_LANGUAGE))) { response.statusCode = 500; var result = caller->respond(response); log:printError("Error responding", err = result); storeOutput(ID_MISSING_ACCEPT_AND_ACCEPT_LANGUAGE_HEADERS, response.statusCode); } else { string mediaType = req.getHeader(HEADER_ACCEPT); string languageType = req.getHeader(HEADER_ACCEPT_LANGUAGE); if (mediaType == CONTENT_TYPE_JSON && languageType == LANGUAGE_TYPE_DE) { websubhub:addWebSubLinkHeader(response, [WebSubHub.subscriptionUrl], WEBSUB_TOPIC_ONE); response.statusCode = 202; var result = caller->respond(response); storeOutput(ID_MATCH_ACCEPT_AND_ACCEPT_LANGUAGE_HEADERS, response.statusCode); if (result is error) { log:printError("Error responding", err = result); } } else { response.statusCode = 406; var result = caller->respond(response); log:printError("Error responding", err = result); storeOutput(ID_MISMATCH_ACCEPT_AND_ACCEPT_LANGUAGE_HEADERS, response.statusCode); } } } resource function get withAcceptHeader(http:Caller caller, http:Request req) { http:Response response = new; string mediaType = req.getHeader(HEADER_ACCEPT); if (mediaType == CONTENT_TYPE_JSON) { websubhub:addWebSubLinkHeader(response, [WebSubHub.subscriptionUrl], WEBSUB_TOPIC_ONE); response.statusCode = 202; var result = caller->respond(response); storeOutput(ID_MATCH_ACCEPT_HEADER, response.statusCode); if (result is error) { log:printError("Error responding", err = result); } } else { response.statusCode = 406; var result = caller->respond(response); log:printError("Error responding", err = result); storeOutput(ID_MISMATCH_ACCEPT_HEADER, response.statusCode); } } resource function get withAcceptLanguageHeader(http:Caller caller, http:Request req) { http:Response response = new; string languageType = req.getHeader(HEADER_ACCEPT_LANGUAGE); if (languageType == LANGUAGE_TYPE_DE) { websubhub:addWebSubLinkHeader(response, [WebSubHub.subscriptionUrl], WEBSUB_TOPIC_ONE); response.statusCode = 202; var result = caller->respond(response); storeOutput(ID_MATCH_ACCEPT_LANGUAGE_HEADER, response.statusCode); if (result is error) { log:printError("Error responding", err = result); } } else { response.statusCode = 406; var result = caller->respond(response); log:printError("Error responding", err = result); storeOutput(ID_MISMATCH_ACCEPT_LANGUAGE_HEADER, response.statusCode); } } } // do nothing for every onNotification functions.Just for SubscriberServiceConfig annotation.Testing for new fields // of SubscriberServiceConfig annotation is done by checking status code of response from initial discovery request @websub:SubscriberServiceConfig { subscribeOnStartUp: true, target: "http://localhost:24080/publisherService/withAcceptAndAcceptLanguageHeaderArray", accept: ["application/json", "application/xml", "text/html"], acceptLanguage: ["de-DE", "de-US"], leaseSeconds: 3600, secret: "Kslk30SNF2AChs2" } service websub:SubscriberService /subcriberMatchingAcceptAndAcceptLanguageHeaderArray on websubSubscriberEPEight { remote function onNotification(websub:Notification notification) {} } @websub:SubscriberServiceConfig { subscribeOnStartUp: true, target: "http://localhost:24080/publisherService/withAcceptAndAcceptLanguageHeaderArray", accept: ["text/csv", "text/plain"], acceptLanguage: ["en-US", "en-CA"], leaseSeconds: 3600, secret: "Kslk30SNF2AChs2" } service websub:SubscriberService /subcriberMisMatchingAcceptAndAcceptLanguageHeaderArray on websubSubscriberEPNine { remote function onNotification(websub:Notification notification) {} } @websub:SubscriberServiceConfig { subscribeOnStartUp: true, target: "http://localhost:24080/publisherService/withAcceptAndAcceptLanguageHeaders", accept: "application/json", acceptLanguage: "de-DE", leaseSeconds: 3600, secret: "Kslk30SNF2AChs2" } service websub:SubscriberService /subcriberMatchingAcceptAndAcceptLanguageHeaders on websubSubscriberEPOne { remote function onNotification(websub:Notification notification) {} } @websub:SubscriberServiceConfig { subscribeOnStartUp: true, target: "http://localhost:24080/publisherService/withAcceptAndAcceptLanguageHeaders", accept: "text/html", acceptLanguage: "de-US", leaseSeconds: 3600, secret: "Kslk30SNF2AChs2" } service websub:SubscriberService /subcriberMisMatchingAcceptAndAcceptLanguageHeaders on websubSubscriberEPTwo { remote function onNotification(websub:Notification notification) {} } @websub:SubscriberServiceConfig { subscribeOnStartUp: true, target: "http://localhost:24080/publisherService/withAcceptHeader", accept: "application/json", leaseSeconds: 3600, secret: "Kslk30SNF2AChs2" } service websub:SubscriberService /subcriberMatchingAcceptHeader on websubSubscriberEPThree { remote function onNotification(websub:Notification notification) {} } @websub:SubscriberServiceConfig { subscribeOnStartUp: true, target: "http://localhost:24080/publisherService/withAcceptHeader", accept: "text/html", leaseSeconds: 3600, secret: "Kslk30SNF2AChs2" } service websub:SubscriberService /subcriberMisMatchingAcceptHeader on websubSubscriberEPFour { remote function onNotification(websub:Notification notification) {} } @websub:SubscriberServiceConfig { subscribeOnStartUp: true, target: "http://localhost:24080/publisherService/withAcceptLanguageHeader", acceptLanguage: "de-DE", leaseSeconds: 3600, secret: "Kslk30SNF2AChs2" } service websub:SubscriberService /subcriberMatchingAcceptLanguageHeader on websubSubscriberEPFive { remote function onNotification(websub:Notification notification) {} } @websub:SubscriberServiceConfig { subscribeOnStartUp: true, target: "http://localhost:24080/publisherService/withAcceptLanguageHeader", acceptLanguage: "de-US", leaseSeconds: 3600, secret: "Kslk30SNF2AChs2" } service websub:SubscriberService /subcriberMisMatchingAcceptLanguageHeader on websubSubscriberEPSix { remote function onNotification(websub:Notification notification) {} } @websub:SubscriberServiceConfig { subscribeOnStartUp: true, target: "http://localhost:24080/publisherService/withAcceptAndAcceptLanguageHeaders", leaseSeconds: 3600, secret: "Kslk30SNF2AChs2" } service websub:SubscriberService /subcriberMissingAcceptAndAcceptLanguageHeaders on websubSubscriberEPSeven { remote function onNotification(websub:Notification notification) {} } @test:Config {} function testMatchingAcceptAndAcceptLanguageHeaderArray() { test:assertEquals(fetchOutput(ID_MATCH_ACCEPT_AND_ACCEPT_LANGUAGE_HEADER_ARRAY), RESPONSE_CODE_ACCEPTED); } @test:Config {} function testMisMatchingAcceptAndAcceptLanguageHeaderArray() { test:assertEquals(fetchOutput(ID_MISMATCH_ACCEPT_AND_ACCEPT_LANGUAGE_HEADER_ARRAY), RESPONSE_CODE_NOT_ACCEPTABLE); } @test:Config {} function testMatchingAcceptAndAcceptLanguageHeaders() { test:assertEquals(fetchOutput(ID_MATCH_ACCEPT_AND_ACCEPT_LANGUAGE_HEADERS), RESPONSE_CODE_ACCEPTED); } @test:Config {} function testMisMatchingAcceptAndAcceptLanguageHeaders() { test:assertEquals(fetchOutput(ID_MISMATCH_ACCEPT_AND_ACCEPT_LANGUAGE_HEADERS), RESPONSE_CODE_NOT_ACCEPTABLE); } @test:Config {} function testMatchingAcceptHeader() { test:assertEquals(fetchOutput(ID_MATCH_ACCEPT_HEADER), RESPONSE_CODE_ACCEPTED); } @test:Config {} function testMisMatchingAcceptHeader() { test:assertEquals(fetchOutput(ID_MISMATCH_ACCEPT_HEADER), RESPONSE_CODE_NOT_ACCEPTABLE); } @test:Config {} function testMatchingAcceptLanguageHeader() { test:assertEquals(fetchOutput(ID_MATCH_ACCEPT_LANGUAGE_HEADER), RESPONSE_CODE_ACCEPTED); } @test:Config {} function testMisMatchingAcceptLanguageHeader() { test:assertEquals(fetchOutput(ID_MISMATCH_ACCEPT_LANGUAGE_HEADER), RESPONSE_CODE_NOT_ACCEPTABLE); } @test:Config {} function testMissingAcceptAndAcceptLanguageHeaders() { test:assertEquals(fetchOutput(ID_MISSING_ACCEPT_AND_ACCEPT_LANGUAGE_HEADERS), RESPONSE_CODE_INTERNAL_SERVER_ERROR); } ================================================ FILE: stdlib-integration-tests/websub-generic/tests/test_custom_subscribers.bal ================================================ // Copyright (c) 2018 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. // // WSO2 Inc. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except // in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. import ballerina/test; import ballerina/http; import ballerina/websub; public type CustomSubWebhookListenerConf record { string host = ""; }; public type CustomSubMockActionEvent record {| string action; |}; public type CustomSubMockDomainEvent record {| string domain; |}; @websub:SubscriberServiceConfig {} service websub:SubscriberService /key on new CustomSubWebhookServerForPayload(23585) { remote function onIntentVerification(websub:Caller caller, websub:IntentVerificationRequest verRequest) { storeOutput(ID_INTENT_VER_REQ_RECEIVED_LOG, "Intent verification request received"); checkpanic caller->accepted(); } remote function onCreated(websub:Notification notification, CustomSubMockActionEvent event) { storeOutput(ID_BY_KEY_CREATED_LOG, "Created Notification Received, action: " + <@untainted>event.action); } remote function onFeature(websub:Notification notification, CustomSubMockDomainEvent event) { storeOutput(ID_BY_KEY_FEATURE_LOG, "Feature Notification Received, domain: " + <@untainted>event.domain); } remote function onStatus(websub:Notification notification, CustomSubMockActionEvent event) { // do nothing - test start up } } @websub:SubscriberServiceConfig {} service websub:SubscriberService /header on new CustomSubWebhookServerForHeader(23686) { remote function onIssue(websub:Notification notification, CustomSubMockActionEvent event) { string msg = "Issue Notification Received, header value: " + <@untainted>notification.getHeader(CUSTOM_SUB_MOCK_HEADER) + " action: " + <@untainted>event.action; storeOutput(ID_BY_HEADER_ISSUE_LOG, msg); } remote function onCommit(websub:Notification notification, CustomSubMockActionEvent event) { string msg = "Commit Notification Received, header value: " + <@untainted>notification.getHeader(CUSTOM_SUB_MOCK_HEADER) + " action: " + <@untainted>event.action; storeOutput(ID_BY_HEADER_COMMIT_LOG, msg); } remote function onStatus(websub:Notification notification, CustomSubMockActionEvent event) { // do nothing - test start up } } @websub:SubscriberServiceConfig {} service websub:SubscriberService /headerAndPayload on new CustomSubWebhookServerForHeaderAndPayload(23787) { remote function onIssueCreated(websub:Notification notification, CustomSubMockActionEvent event) { string msg = "Issue Created Notification Received, header value: " + <@untainted>notification.getHeader(CUSTOM_SUB_MOCK_HEADER) + " action: " + <@untainted>event.action; storeOutput(ID_BY_HEADER_AND_PAYLOAD_ISSUE_CREATED_LOG, msg); } remote function onFeaturePull(websub:Notification notification, CustomSubMockDomainEvent event) { string msg = "Feature Pull Notification Received, header value: " + <@untainted>notification.getHeader(CUSTOM_SUB_MOCK_HEADER) + " domain: " + <@untainted>event.domain; storeOutput(ID_BY_HEADER_AND_PAYLOAD_FEATURE_PULL_LOG, msg); } remote function onHeaderOnly(websub:Notification notification, CustomSubMockActionEvent event) { string msg = "HeaderOnly Notification Received, header value: " + <@untainted>notification.getHeader(CUSTOM_SUB_MOCK_HEADER) + " action: " + <@untainted>event.action; storeOutput(ID_BY_HEADER_AND_PAYLOAD_HEADER_ONLY_LOG, msg); } remote function onKeyOnly(websub:Notification notification, CustomSubMockActionEvent event) { string msg = "KeyOnly Notification Received, header value: " + <@untainted>notification.getHeader(CUSTOM_SUB_MOCK_HEADER) + " action: " + <@untainted>event.action; storeOutput(ID_BY_HEADER_AND_PAYLOAD_KEY_ONLY_LOG, msg); } remote function onStatus(websub:Notification notification, CustomSubMockActionEvent event) { // do nothing - test start up } } /////////////////// Specific Webhook for dispatching by key /////////////////// public class CustomSubWebhookServerForPayload { private websub:Listener websubListener; public function init(int port, CustomSubWebhookListenerConf? config = ()) { websub:ExtensionConfig extensionConfig = { topicIdentifier: websub:TOPIC_ID_PAYLOAD_KEY, payloadKeyResourceMap: { "action" : { "created" : ["onCreated", CustomSubMockActionEvent], "deleted" : ["onDeleted", CustomSubMockActionEvent], "statuscheck" : ["onStatus", CustomSubMockActionEvent] }, "domain" : { "issue" : ["onIssue", CustomSubMockDomainEvent], "feature" : ["onFeature", CustomSubMockDomainEvent] } } }; string host = config is () ? "" : config.host; websub:SubscriberListenerConfiguration sseConfig = { host: host, extensionConfig: extensionConfig }; self.websubListener = new(port, sseConfig); } public function attach(websub:SubscriberService s, string[]|string? name = ()) returns error? { return self.websubListener.attach(s, name); } public function detach(websub:SubscriberService s) returns error? { return self.websubListener.detach(s); } public function 'start() returns error? { return self.websubListener.'start(); } public function gracefulStop() returns error? { return (); } public function immediateStop() returns error? { return self.websubListener.immediateStop(); } } /////////////////// Specific Webhook for dispatching by header /////////////////// public class CustomSubWebhookServerForHeader { private websub:Listener websubListener; public function init(int port, CustomSubWebhookListenerConf? config = ()) { websub:ExtensionConfig extensionConfig = { topicIdentifier: websub:TOPIC_ID_HEADER, topicHeader: CUSTOM_SUB_MOCK_HEADER, headerResourceMap: { "issue" : ["onIssue", CustomSubMockActionEvent], "commit" : ["onCommit", CustomSubMockActionEvent], "status" : ["onStatus", CustomSubMockActionEvent] } }; string host = config is () ? "" : config.host; websub:SubscriberListenerConfiguration sseConfig = { host: host, extensionConfig: extensionConfig }; self.websubListener = new(port, sseConfig); } public function attach(websub:SubscriberService s, string[]|string? name = ()) returns error? { return self.websubListener.attach(s, name); } public function detach(websub:SubscriberService s) returns error? { return self.websubListener.detach(s); } public function 'start() returns error? { return self.websubListener.'start(); } public function gracefulStop() returns error? { return (); } public function immediateStop() returns error? { return self.websubListener.immediateStop(); } } /////////////////// Specific Webhook for dispatching by header and payload /////////////////// public class CustomSubWebhookServerForHeaderAndPayload { private websub:Listener websubListener; public function init(int port, CustomSubWebhookListenerConf? config = ()) { websub:ExtensionConfig extensionConfig = { topicIdentifier: websub:TOPIC_ID_HEADER_AND_PAYLOAD, topicHeader: CUSTOM_SUB_MOCK_HEADER, headerResourceMap: { "headeronly" : ["onHeaderOnly", CustomSubMockActionEvent], "status" : ["onStatus", CustomSubMockActionEvent] }, payloadKeyResourceMap: { "action" : { "keyonly" : ["onKeyOnly", CustomSubMockActionEvent] }, "domain" : { "domainkeyonly" : ["onDomainKeyOnly", CustomSubMockDomainEvent] } }, headerAndPayloadKeyResourceMap: { "issue" : { "action" : { "created" : ["onIssueCreated", CustomSubMockActionEvent], "deleted" : ["onIssueDeleted", CustomSubMockActionEvent] } }, "pull" : { "domain" : { "bugfix" : ["onBugFixPull", CustomSubMockDomainEvent], "feature" : ["onFeaturePull", CustomSubMockDomainEvent] } } } }; string host = config is () ? "" : config.host; websub:SubscriberListenerConfiguration sseConfig = { host: host, extensionConfig: extensionConfig }; self.websubListener = new(port, sseConfig); } public function attach(websub:SubscriberService s, string[]|string? name = ()) returns error? { return self.websubListener.attach(s, name); } public function detach(websub:SubscriberService s) returns error? { return self.websubListener.detach(s); } public function 'start() returns error? { return self.websubListener.'start(); } public function gracefulStop() returns error? { return (); } public function immediateStop() returns error? { return self.websubListener.immediateStop(); } } @test:Config {} function testOnIntentVerificationInvocation() { http:Client clientEndpoint = new ("http://localhost:23585"); var response = clientEndpoint->get("/key"); test:assertEquals(fetchOutput(ID_INTENT_VER_REQ_RECEIVED_LOG), INTENT_VER_REQ_RECEIVED_LOG); } @test:Config { dependsOn: [testOnIntentVerificationInvocation] } function testDispatchingByKey() { http:Client clientEndpoint = new ("http://localhost:23585"); json jsonPayload1 = {action: "created"}; json jsonPayload2 = {domain: "feature"}; http:Request req1 = new; req1.addHeader(http:CONTENT_TYPE, CONTENT_TYPE_JSON); req1.setJsonPayload(jsonPayload1); http:Request req2 = new; req2.addHeader(http:CONTENT_TYPE, CONTENT_TYPE_JSON); req2.setJsonPayload(jsonPayload2); var response = clientEndpoint->post("/key", req1); HttpResponseDetails responseDetails = fetchHttpResponse(response); test:assertEquals(responseDetails.statusCode, http:STATUS_ACCEPTED, msg = "Response code mismatched"); test:assertEquals(fetchOutput(ID_BY_KEY_CREATED_LOG), BY_KEY_CREATED_LOG); response = clientEndpoint->post("/key", req2); responseDetails = fetchHttpResponse(response); test:assertEquals(responseDetails.statusCode, http:STATUS_ACCEPTED, msg = "Response code mismatched"); test:assertEquals(fetchOutput(ID_BY_KEY_FEATURE_LOG), BY_KEY_FEATURE_LOG); } @test:Config { dependsOn: [testDispatchingByKey], enable:false } function testDispatchingByHeader() { http:Client clientEndpoint = new ("http://localhost:23686"); json jsonPayload1 = {action: "deleted"}; json jsonPayload2 = {action: "created"}; http:Request req1 = new; req1.addHeader(http:CONTENT_TYPE, CONTENT_TYPE_JSON); req1.addHeader(CUSTOM_SUB_MOCK_HEADER, "issue"); req1.setJsonPayload(jsonPayload1); http:Request req2 = new; req2.addHeader(http:CONTENT_TYPE, CONTENT_TYPE_JSON); req2.addHeader(CUSTOM_SUB_MOCK_HEADER, "commit"); req2.setJsonPayload(jsonPayload2); var response1 = clientEndpoint->post("/header", req1); HttpResponseDetails responseDetails1 = fetchHttpResponse(response1); test:assertEquals(responseDetails1.statusCode, http:STATUS_ACCEPTED, msg = "Response code mismatched"); test:assertEquals(fetchOutput(ID_BY_HEADER_ISSUE_LOG), BY_HEADER_ISSUE_LOG); var response2 = clientEndpoint->post("/header", req2); HttpResponseDetails responseDetails2 = fetchHttpResponse(response2); test:assertEquals(responseDetails2.statusCode, http:STATUS_ACCEPTED, msg = "Response code mismatched"); test:assertEquals(fetchOutput(ID_BY_HEADER_COMMIT_LOG), BY_HEADER_COMMIT_LOG); } @test:Config {enable: false} function testDispatchingByHeaderAndPayloadKey() { http:Client clientEndpoint = new ("http://localhost:23787"); json jsonPayload1 = {action: "created"}; json jsonPayload2 = {domain: "feature"}; http:Request req1 = new; req1.addHeader(http:CONTENT_TYPE, CONTENT_TYPE_JSON); req1.addHeader(CUSTOM_SUB_MOCK_HEADER, "issue"); req1.setJsonPayload(jsonPayload1); http:Request req2 = new; req2.addHeader(http:CONTENT_TYPE, CONTENT_TYPE_JSON); req2.addHeader(CUSTOM_SUB_MOCK_HEADER, "pull"); req2.setJsonPayload(jsonPayload2); var response1 = clientEndpoint->post("/headerAndPayload", req1); HttpResponseDetails responseDetails1 = fetchHttpResponse(response1); test:assertEquals(responseDetails1.statusCode, http:STATUS_ACCEPTED, msg = "Response code mismatched"); test:assertEquals(fetchOutput(ID_BY_HEADER_AND_PAYLOAD_ISSUE_CREATED_LOG), BY_HEADER_AND_PAYLOAD_ISSUE_CREATED_LOG); var response2 = clientEndpoint->post("/headerAndPayload", req2); HttpResponseDetails responseDetails2 = fetchHttpResponse(response2); test:assertEquals(responseDetails2.statusCode, http:STATUS_ACCEPTED, msg = "Response code mismatched"); test:assertEquals(fetchOutput(ID_BY_HEADER_AND_PAYLOAD_FEATURE_PULL_LOG), BY_HEADER_AND_PAYLOAD_FEATURE_PULL_LOG); } @test:Config {enable: false} function testDispatchingByHeaderAndPayloadKeyForOnlyHeader() { http:Client clientEndpoint = new ("http://localhost:23787"); json jsonPayload = {action: "header_only"}; http:Request req = new; req.addHeader(http:CONTENT_TYPE, CONTENT_TYPE_JSON); req.addHeader(CUSTOM_SUB_MOCK_HEADER, "headeronly"); req.setJsonPayload(jsonPayload); var response = clientEndpoint->post("/headerAndPayload", req); HttpResponseDetails responseDetails = fetchHttpResponse(response); test:assertEquals(responseDetails.statusCode, http:STATUS_ACCEPTED, msg = "Response code mismatched"); test:assertEquals(fetchOutput(ID_BY_HEADER_AND_PAYLOAD_HEADER_ONLY_LOG), BY_HEADER_AND_PAYLOAD_HEADER_ONLY_LOG); } @test:Config { dependsOn: [testDispatchingByHeaderAndPayloadKeyForOnlyHeader], enable:false } function testDispatchingByHeaderAndPayloadKeyForOnlyKey() { http:Client clientEndpoint = new ("http://localhost:23787"); json jsonPayload = {action: "keyonly"}; http:Request req = new; req.addHeader(http:CONTENT_TYPE, CONTENT_TYPE_JSON); req.addHeader(CUSTOM_SUB_MOCK_HEADER, "key_only"); req.setJsonPayload(jsonPayload); var response = clientEndpoint->post("/headerAndPayload", req); HttpResponseDetails responseDetails = fetchHttpResponse(response); test:assertEquals(responseDetails.statusCode, http:STATUS_ACCEPTED, msg = "Response code mismatched"); test:assertEquals(fetchOutput(ID_BY_HEADER_AND_PAYLOAD_KEY_ONLY_LOG), BY_HEADER_AND_PAYLOAD_KEY_ONLY_LOG); } ================================================ FILE: stdlib-integration-tests/websub-generic/tests/test_different_content_type_subscribers.bal ================================================ // Copyright (c) 2018 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. // // WSO2 Inc. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except // in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. import ballerina/mime; import ballerina/test; import ballerina/lang.runtime as runtime; import ballerina/http; import ballerina/websub; listener websub:Listener websubDifContentTypeEP = new websub:Listener(23282); @websub:SubscriberServiceConfig { target: ["http://localhost:23191/websub/hub", "http://one.websub.topic.com"], leaseSeconds: 3000, secret: "Kslk30SNF2AChs2" } service websub:SubscriberService /websub on websubDifContentTypeEP { remote function onNotification (websub:Notification notification) { if (notification.getContentType() == mime:TEXT_PLAIN) { var payload = notification.getTextPayload(); if (payload is string) { storeOutput(ID_TEXT_SUBSCRIBER_ONE, "Text WebSub Notification Received by websubSubscriber: " + <@untainted>payload); } else { panic payload; } } else if (notification.getContentType() == mime:APPLICATION_XML) { var payload = notification.getXmlPayload(); if (payload is xml) { storeOutput(ID_XML_SUBSCRIBER_ONE, "XML WebSub Notification Received by websubSubscriber: " + <@untainted>payload.toString()); } else { panic payload; } } else if (notification.getContentType() == mime:APPLICATION_JSON) { var payload = notification.getJsonPayload(); if (payload is json) { storeOutput(ID_JSON_SUBSCRIBER_ONE, "JSON WebSub Notification Received by websubSubscriber: " + <@untainted>payload.toJsonString()); } else { panic payload; } } } } @websub:SubscriberServiceConfig { path:"/websubTwo", subscribeOnStartUp:true, target: ["http://localhost:23191/websub/hub", "http://one.websub.topic.com"], leaseSeconds: 1000 } service websub:SubscriberService /websubTwo on websubDifContentTypeEP { remote function onNotification (websub:Notification notification) { if (notification.getContentType() == mime:TEXT_PLAIN) { var payload = notification.getTextPayload(); if (payload is string) { storeOutput(ID_TEXT_SUBSCRIBER_TWO, "Text WebSub Notification Received by websubSubscriberTwo: " + <@untainted>payload); } else { panic payload; } } else if (notification.getContentType() == mime:APPLICATION_XML) { var payload = notification.getXmlPayload(); if (payload is xml) { storeOutput(ID_XML_SUBSCRIBER_TWO, "XML WebSub Notification Received by websubSubscriberTwo: " + <@untainted>payload.toString()); } else { panic payload; } } else if (notification.getContentType() == mime:APPLICATION_JSON) { var payload = notification.getJsonPayload(); if (payload is json) { storeOutput(ID_JSON_SUBSCRIBER_TWO, "JSON WebSub Notification Received by websubSubscriberTwo: " + <@untainted>payload.toJsonString()); } else { panic payload; } } } } @test:Config { dependsOn: [testUnsubscription] } function testTextContentReceiptForInternalHub() { sendSubscriptionAndIntentVerificationRequest(COMMON_PATH + "23282", HUB_MODE_INTERNAL, TYPE_STRING); runtime:sleep(5); test:assertEquals(fetchOutput(ID_TEXT_SUBSCRIBER_ONE), TEXT_INTERNAL_HUB_NOTIFICATION_SUBSCRIBER_ONE_LOG); test:assertEquals(fetchOutput(ID_TEXT_SUBSCRIBER_TWO), TEXT_INTERNAL_HUB_NOTIFICATION_SUBSCRIBER_TWO_LOG); } @test:Config { dependsOn: [testTextContentReceiptForInternalHub] } function testTextContentReceiptForRemoteHub() { sendSubscriptionAndIntentVerificationRequest(COMMON_PATH + SKIP_SUBSCRIBER_CHECK, HUB_MODE_REMOTE, TYPE_STRING); runtime:sleep(5); test:assertEquals(fetchOutput(ID_TEXT_SUBSCRIBER_ONE), TEXT_REMOTE_HUB_NOTIFICATION_SUBSCRIBER_ONE_LOG); test:assertEquals(fetchOutput(ID_TEXT_SUBSCRIBER_TWO), TEXT_REMOTE_HUB_NOTIFICATION_SUBSCRIBER_TWO_LOG); } @test:Config { dependsOn: [testTextContentReceiptForRemoteHub] } function testXmlContentReceiptForInternalHub() { sendSubscriptionAndIntentVerificationRequest(COMMON_PATH + SKIP_SUBSCRIBER_CHECK, HUB_MODE_INTERNAL, TYPE_XML); runtime:sleep(5); test:assertEquals(fetchOutput(ID_XML_SUBSCRIBER_ONE), XML_INTERNAL_HUB_NOTIFICATION_SUBSCRIBER_ONE_LOG); test:assertEquals(fetchOutput(ID_XML_SUBSCRIBER_TWO), XML_INTERNAL_HUB_NOTIFICATION_SUBSCRIBER_TWO_LOG); } @test:Config { dependsOn: [testXmlContentReceiptForInternalHub] } function testXmlContentReceiptForRemoteHub() { sendSubscriptionAndIntentVerificationRequest(COMMON_PATH + SKIP_SUBSCRIBER_CHECK, HUB_MODE_REMOTE, TYPE_XML); runtime:sleep(5); test:assertEquals(fetchOutput(ID_XML_SUBSCRIBER_ONE), XML_REMOTE_HUB_NOTIFICATION_SUBSCRIBER_ONE_LOG); test:assertEquals(fetchOutput(ID_XML_SUBSCRIBER_TWO), XML_REMOTE_HUB_NOTIFICATION_SUBSCRIBER_TWO_LOG); } @test:Config { dependsOn: [testXmlContentReceiptForRemoteHub] } function testJsonContentReceiptForInternalHub() { sendSubscriptionAndIntentVerificationRequest(COMMON_PATH + SKIP_SUBSCRIBER_CHECK, HUB_MODE_INTERNAL, TYPE_JSON); runtime:sleep(10); test:assertEquals(fetchOutput(ID_JSON_SUBSCRIBER_ONE), JSON_INTERNAL_HUB_NOTIFICATION_SUBSCRIBER_ONE_LOG); test:assertEquals(fetchOutput(ID_JSON_SUBSCRIBER_TWO), JSON_INTERNAL_HUB_NOTIFICATION_SUBSCRIBER_TWO_LOG); } @test:Config { dependsOn: [testJsonContentReceiptForInternalHub] } function testJsonContentReceiptForRemoteHub() { sendSubscriptionAndIntentVerificationRequest(COMMON_PATH + SKIP_SUBSCRIBER_CHECK, HUB_MODE_REMOTE, TYPE_JSON); runtime:sleep(10); test:assertEquals(fetchOutput(ID_JSON_SUBSCRIBER_ONE), JSON_REMOTE_HUB_NOTIFICATION_SUBSCRIBER_ONE_LOG); test:assertEquals(fetchOutput(ID_JSON_SUBSCRIBER_TWO), JSON_REMOTE_HUB_NOTIFICATION_SUBSCRIBER_TWO_LOG); } function sendSubscriptionAndIntentVerificationRequest (string path, string mode, string contentType) { http:Client clientEndpoint = new ("http://localhost:23080"); json jsonPayload = {mode: mode, content_type: contentType}; http:Request req = new; req.addHeader(http:CONTENT_TYPE, CONTENT_TYPE_JSON); req.setJsonPayload(jsonPayload); var response = clientEndpoint->post(path, req); } ================================================ FILE: stdlib-integration-tests/websub-generic/tests/test_multiple_subscribers.bal ================================================ // Copyright (c) 2018 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. // // WSO2 Inc. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except // in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. import ballerina/http; import ballerina/test; import ballerina/lang.runtime as runtime; import ballerina/websub; listener websub:Listener multipleSubTestWebsubEP = new websub:Listener(23383); // Following listner has no services attached with and it should not fail the listener start listener websub:Listener websubEndPointWithNoAttachedServices = new websub:Listener(23384); @websub:SubscriberServiceConfig { target: ["http://localhost:23191/websub/hub", "http://three.websub.topic.com"], leaseSeconds: 3600, secret: "Kslk30SNF2AChs2" } service websub:SubscriberService /websub on multipleSubTestWebsubEP { remote function onNotification (websub:Notification notification) { var payload = notification.getJsonPayload(); if (payload is json) { storeOutput(ID_INTERNAL_HUB_NOTIFICATION_SUBSCRIBER_ONE_LOG, "WebSub Notification Received by One: " + <@untainted>payload.toJsonString()); } else { panic payload; } } } @websub:SubscriberServiceConfig { target: "http://localhost:23080/publisherTwo/discover", leaseSeconds: 1200, secret: "SwklSSf42DLA" } service websub:SubscriberService /websubTwo on multipleSubTestWebsubEP { remote function onNotification (websub:Notification notification) { var payload = notification.getJsonPayload(); if (payload is json) { storeOutput(ID_INTERNAL_HUB_NOTIFICATION_SUBSCRIBER_TWO_LOG, "WebSub Notification Received by Two: " + <@untainted>payload.toJsonString()); } else { panic payload; } } } @test:Config { dependsOn: [testJsonContentReceiptForRemoteHub], enable: false } function testContentReceipt() { http:Client clientEndpoint = new ("http://localhost:23080"); json jsonPayload = {mode: "internal", content_type: "json"}; http:Request req = new; req.addHeader(http:CONTENT_TYPE, CONTENT_TYPE_JSON); req.setJsonPayload(jsonPayload); var response = clientEndpoint->post("/publisherTwo/notify", req); runtime:sleep(5); test:assertEquals(fetchOutput(ID_INTERNAL_HUB_NOTIFICATION_SUBSCRIBER_ONE_LOG), INTERNAL_HUB_NOTIFICATION_SUBSCRIBER_ONE_LOG); test:assertEquals(fetchOutput(ID_INTERNAL_HUB_NOTIFICATION_SUBSCRIBER_TWO_LOG), INTERNAL_HUB_NOTIFICATION_SUBSCRIBER_TWO_LOG); } ================================================ FILE: stdlib-integration-tests/websub-generic/tests/test_redirected_subscribers.bal ================================================ // Copyright (c) 2018 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. // // WSO2 Inc. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except // in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. import ballerina/io; import ballerina/test; import ballerina/websub; listener websub:Listener redirectWebsubEP = new websub:Listener(23484); @websub:SubscriberServiceConfig { subscribeOnStartUp:true, target: "http://localhost:23081/original/one", leaseSeconds: 3600, secret: "Kslk30SNF2AChs2", publisherClientConfig: { followRedirects: { enabled: true } }, hubClientConfig: { followRedirects: { enabled: true } } } service websub:SubscriberService /websub on redirectWebsubEP { remote function onNotification (websub:Notification notification) { var payload = notification.getJsonPayload(); if (payload is json) { io:println("WebSub Notification Received: " + payload.toJsonString()); } else { panic payload; } } } @websub:SubscriberServiceConfig { target: "http://localhost:23081/original/two", leaseSeconds: 1200, secret: "SwklSSf42DLA", publisherClientConfig: { followRedirects: { enabled: true } }, hubClientConfig: { followRedirects: { enabled: true } } } service websub:SubscriberService /websubTwo on redirectWebsubEP { remote function onNotification (websub:Notification notification) { var payload = notification.getJsonPayload(); if (payload is json) { io:println("WebSub Notification Received: " + payload.toJsonString()); } else { panic payload; } } } @test:Config { dependsOn: [testJsonContentReceiptForRemoteHub], enable: false } function testTopicMovedPermanentlyAndHubTemporaryRedirect() { test:assertEquals(fetchOutput(ID_REDIRECT_SUBSCRIBER_ONE_LOG), REDIRECT_SUBSCRIBER_ONE_LOG); } @test:Config { dependsOn: [testJsonContentReceiptForRemoteHub], enable: false } function testTopicRedirectFoundAndHubPermanentRedirect() { test:assertEquals(fetchOutput(ID_REDIRECT_SUBSCRIBER_TWO_LOG), REDIRECT_SUBSCRIBER_TWO_LOG); } ================================================ FILE: stdlib-integration-tests/websub-generic/tests/test_subscriber.bal ================================================ // Copyright (c) 2018 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. // // WSO2 Inc. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except // in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. import ballerina/io; import ballerina/http; import ballerina/lang.runtime as runtime; import ballerina/stringutils; import ballerina/test; import ballerina/websub; listener websub:Listener websubEP = new websub:Listener(23181, { host: "0.0.0.0" }); @websub:SubscriberServiceConfig { subscribeOnStartUp:true, target: ["http://localhost:23191/websub/hub", "http://one.websub.topic.com"] } service websub:SubscriberService /websub on websubEP { remote function onNotification (websub:Notification notification) { json payload = checkpanic notification.getJsonPayload(); storeOutput(ID_HUB_NOTIFICATION_LOG, "WebSub Notification Received: " + <@untainted>payload.toJsonString()); io:println("WebSub Notification Received: " + <@untainted>payload.toJsonString()); } } @websub:SubscriberServiceConfig { target: ["http://localhost:23191/websub/hub", "http://one.websub.topic.com"], leaseSeconds: 3650, secret: "Kslk30SNF2AChs2" } service websub:SubscriberService /subscriberWithNoPathInAnnot on websubEP { remote function onIntentVerification (websub:Caller caller, websub:IntentVerificationRequest request) { http:Response response = request.buildSubscriptionVerificationResponse("http://one.websub.topic.com"); if (response.statusCode == 202) { storeOutput(ID_EXPLICIT_INTENT_VERIFICATION_LOG, "Intent verified explicitly for subscription change request"); } else { io:println("Intent verification denied explicitly for subscription change request"); } var result = caller->respond(<@untainted> response); if (result is error) { io:println("Error responding to intent verification request: ", result); } } remote function onNotification (websub:Notification notification) { string payload = checkpanic notification.getTextPayload(); storeOutput(ID_HUB_NOTIFICATION_LOG_TWO, "WebSub Notification Received by Two: " + <@untainted>payload); io:println("WebSub Notification Received by Two: " + <@untainted>payload); } } string subscriberThreeTopic = "http://one.websub.topic.com"; @websub:SubscriberServiceConfig { path:"/websubThree", subscribeOnStartUp:true, target: ["http://localhost:23191/websub/hub", subscriberThreeTopic], leaseSeconds: 300, callback: "http://localhost:23181/websubThree?topic=" + subscriberThreeTopic + "&fooVal=barVal", secret: "Xaskdnfe234" } service websub:SubscriberService /websubThree on websubEP { remote function onNotification (websub:Notification notification) { string payload = checkpanic notification.getTextPayload(); io:println("WebSub Notification Received by Three: ", payload); io:println("Query Params: ", notification.getQueryParams()); storeOutput(ID_QUERY_PARAM_LOG, "Query Params: " + <@untainted>notification.getQueryParams().toString()); } } @test:Config {} function testSubscriptionAndAutomaticIntentVerification() { test:assertEquals(fetchOutput(ID_EXPLICIT_INTENT_VERIFICATION_LOG), EXPLICIT_INTENT_VERIFICATION_LOG); } @test:Config { dependsOn: [testSubscriptionAndAutomaticIntentVerification] } function testContentInternalHubNotification() { http:Client clientEndpoint = new ("http://localhost:23080"); json jsonPayload = {mode: "internal", content_type: "json"}; http:Request req = new; req.addHeader(http:CONTENT_TYPE, CONTENT_TYPE_JSON); req.setJsonPayload(jsonPayload); var response = clientEndpoint->post("/publisher/notify/23181", req); runtime:sleep(5); test:assertEquals(fetchOutput(ID_HUB_NOTIFICATION_LOG), INTERNAL_HUB_NOTIFICATION_LOG); test:assertEquals(fetchOutput(ID_HUB_NOTIFICATION_LOG_TWO), INTERNAL_HUB_NOTIFICATION_LOG_TWO); } @test:Config { dependsOn: [testContentInternalHubNotification] } function testContentRemoteHubNotification() { http:Client clientEndpoint = new ("http://localhost:23080"); json jsonPayload = {mode: "remote", content_type: "json"}; http:Request req = new; req.addHeader(http:CONTENT_TYPE, CONTENT_TYPE_JSON); req.setJsonPayload(jsonPayload); var response = clientEndpoint->post("/publisher/notify/23181", req); runtime:sleep(5); test:assertEquals(fetchOutput(ID_HUB_NOTIFICATION_LOG), REMOTE_HUB_NOTIFICATION_LOG); test:assertEquals(fetchOutput(ID_HUB_NOTIFICATION_LOG_TWO), REMOTE_HUB_NOTIFICATION_LOG_TWO); } @test:Config { dependsOn: [testContentRemoteHubNotification] } function testContentReceiptForCallbackWithQueryParams() { test:assertEquals(fetchOutput(ID_QUERY_PARAM_LOG), QUERY_PARAM_LOG); } @test:Config { dependsOn: [testContentReceiptForCallbackWithQueryParams] } function testRemoteTopicRegistration() { http:Client clientEndpoint = new ("http://localhost:23191"); http:Request req = new; req.setPayload("hub.mode=subscribe&hub.topic=http%3A//two.websub.topic.com&hub.callback=http%3A//localhost%3A23181/websub"); var s = req.setContentType(CONTENT_TYPE_FORM_URL_ENCODED); req.addHeader(http:CONTENT_TYPE, "application/x-www-form-urlencoded"); var response = clientEndpoint->post("/websub/hub", req); if (response is http:Response) { test:assertEquals(response.statusCode, http:STATUS_ACCEPTED, msg = "Response code mismatched"); } else { test:assertFail(msg = "Unexpected response"); } } @test:Config { dependsOn: [testRemoteTopicRegistration] } function testSubscriberDetailsRetrievalFromHub() { http:Client clientEndpoint = new ("http://localhost:23080"); http:Request req = new; req.setHeader("x-topic", "http://one.websub.topic.com"); req.setHeader(http:CONTENT_TYPE, CONTENT_TYPE_JSON); var response = clientEndpoint->get("/publisher/topicInfo", req); HttpResponseDetails httpResponseDetails = fetchHttpResponse(response); test:assertEquals(httpResponseDetails.statusCode, http:STATUS_OK); test:assertTrue(stringutils:contains(httpResponseDetails.responseMessage, "\"callback\":\"http://localhost:23181/websub\"")); test:assertTrue( stringutils:contains(httpResponseDetails.responseMessage, "\"callback\":\"http://localhost:23181/subscriberWithNoPathInAnnot\"") ); test:assertTrue( stringutils:contains(httpResponseDetails.responseMessage, "\"callback\":\"http://localhost:23181/websubThree?topic=http://one.websub.topic.com&fooVal=barVal\"") ); } @test:Config { dependsOn: [testSubscriberDetailsRetrievalFromHub] } function testAvailableTopicsRetrievalFromHub() { http:Client clientEndpoint = new ("http://localhost:23080"); http:Request req = new; var response = clientEndpoint->get("/publisher/topicInfo", req); HttpResponseDetails httpResponseDetails = fetchHttpResponse(response); io:println(httpResponseDetails); string expectedData = "{\"Topic_1\":\"http://one.websub.topic.com\", " + "\"Topic_2\":\"http://three.websub.topic.com\", \"Topic_3\":\"http://four.websub.topic.com\", " + "\"Topic_4\":\"http://one.redir.topic.com\", \"Topic_5\":\"http://two.redir.topic.com\", " + "\"Topic_6\":\"http://two.websub.topic.com\"}"; test:assertEquals(httpResponseDetails.statusCode, http:STATUS_OK); test:assertEquals(httpResponseDetails.responseMessage, expectedData); } @test:Config { before: sendUnsubscription, dependsOn: [testAvailableTopicsRetrievalFromHub] } function testUnsubscription() { http:Client clientEndpoint = new ("http://localhost:23080"); var response = clientEndpoint->get("/publisher/unsubscribe"); HttpResponseDetails responseDetails = fetchHttpResponse(response); test:assertEquals(responseDetails.responseMessage, "unsubscription successful", msg = "Mismatched"); json jsonPayload = {mode: "internal", content_type: "json"}; http:Request req = new; req.addHeader(http:CONTENT_TYPE, CONTENT_TYPE_JSON); req.setJsonPayload(jsonPayload); var postResponse = clientEndpoint->post("/publisher/notify/skip_subscriber_check", req); runtime:sleep(5); if (postResponse is http:Response) { test:assertEquals(postResponse.statusCode, http:STATUS_ACCEPTED, msg = "Response code mismatched"); } else { test:assertFail(msg = "Unexpected response"); } } ================================================ FILE: stdlib-integration-tests/websub-generic/tests/test_subscriber_startup.bal ================================================ // Copyright (c) 2019 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/log; import ballerina/test; import ballerina/io; import ballerina/config; import ballerina/websub; import ballerina/websubhub; listener http:Listener testSubscriber = new(23386); listener http:Listener testHub = new(23190); service websub:SubscriberService /subscriber on testSubscriber { resource function get 'start(http:Caller caller, http:Request request) returns error? { websub:Listener l1 = new(23387); websub:Listener l2 = new(23387); string responseMsg = ""; var l1Error = l1.'start(); if (l1Error is error) { log:printError("listener_1 has not started"); string errMsg = l1Error.message(); return caller->respond(errMsg); } log:print("listener_1 has started"); var l2Error = l2.'start(); if (l2Error is error) { log:printError("listener_2 has not started"); responseMsg = l2Error.message(); } else { responseMsg = "listener_2 has started"; } l1Error = l1.gracefulStop(); l2Error = l2.gracefulStop(); return caller->respond(responseMsg); } } service /startupHub on testHub { resource function get startup(http:Caller caller, http:Request request) returns error? { http:Listener lis0 = new (23391); http:Listener lis1 = new (23392); websubhub:Hub|websubhub:HubStartedUpError|websubhub:HubStartupError res = websubhub:startHub(lis0, "/websub", "/hub", "/pub", publicUrl = "https://localhost:23391"); if (res is websubhub:Hub) { if (res.publishUrl != "https://localhost:23391/websub/pub" || res.subscriptionUrl != "https://localhost:23391/websub/hub") { return caller->respond("invalid publishUrl and subscriptionUrl"); } } else { io:println(res); return caller->respond("hub(lis0) start up error"); } // testHubStartUpWhenStarted websubhub:Hub|websubhub:HubStartedUpError|websubhub:HubStartupError res2 = websubhub:startHub(lis1); if !(res2 is websubhub:HubStartedUpError) || res2.startedUpHub !== res { return caller->respond("seperate hub(lis1) has started"); } // testHubShutdownAndStart websubhub:Hub hub = checkpanic res; error? err = hub.stop(); if (err is error) { io:println(err); return caller->respond("hub(lis0) stop error"); } http:Listener lis2 = new (23393); res2 = websubhub:startHub(lis2); if res2 is websubhub:Hub { string responseMsg = res2.publishUrl == "http://localhost:23393/publish" && res2.subscriptionUrl == "http://localhost:23393/" ? "hub(lis2) start successfully" : "incorrect hub(lis2) has started"; err = res2.stop(); return caller->respond(responseMsg); } return caller->respond("hub(lis2) start up error"); } resource function get testPublisherAndSubscriptionInvalidSameResourcePath(http:Caller caller, http:Request request) returns error? { http:Listener lis = new (23394); websubhub:Hub|websubhub:HubStartedUpError|websubhub:HubStartupError res = websubhub:startHub(lis, "/websub", "/hub", "/hub"); var err = lis.gracefulStop(); if (res is websubhub:HubStartupError) { return caller->respond(res.message()); } return caller->respond("Unexpected result"); } } @test:Config { //dependsOn: [testJsonContentReceiptForRemoteHub] } function testMultipleSubscribersStartUpInSamePort() { http:Client clientEndpoint = new ("http://0.0.0.0:23386"); var response = clientEndpoint->get("/subscriber/start"); HttpResponseDetails responseDetails = fetchHttpResponse(response); test:assertEquals(responseDetails.statusCode, http:STATUS_OK, msg = "Response code mismatched"); string expectedResponseMsg = "failed to start server connector '0.0.0.0:23387': Address already in use: bind"; if (config:getAsString("OS_TYPE") == "windows") { test:assertEquals(responseDetails.responseMessage, expectedResponseMsg); } else { expectedResponseMsg = "failed to start server connector '0.0.0.0:23387': Address already in use"; test:assertEquals(responseDetails.responseMessage, expectedResponseMsg); } } ================================================ FILE: stdlib-integration-tests/websub-generic/tests/test_unsubscription_client.bal ================================================ // Copyright (c) 2018 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. // // WSO2 Inc. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except // in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. import ballerina/io; import ballerina/lang.runtime as runtime; import ballerina/websub; function sendUnsubscription() { // This is the client used to send subscription and unsubscription requests. websub:SubscriptionClient websubHubUnsubscribeClientEP = new ("http://localhost:23191/websub/hub"); // Send unsubscription request for the subscriber service. websub:SubscriptionChangeRequest unsubscriptionRequest = { topic: "http://one.websub.topic.com", callback: "http://localhost:23181/websub" }; var response = websubHubUnsubscribeClientEP->unsubscribe(unsubscriptionRequest); if (response is websub:SubscriptionChangeResponse) { io:println("Unsubscription Request successful at Hub [" + response.hub + "] for Topic [" + response.topic + "]"); } else { io:println("Error occurred with Unsubscription Request: ", response.cause()); } // Confirm unsubscription - no notifications should be received. runtime:sleep(5); } ================================================ FILE: stdlib-integration-tests/websub-generic/tests/utils.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; type HttpResponseDetails record { int statusCode; string responseMessage; }; map<(anydata|error)> outputs = {}; public function storeOutput(string key, any|error... s) { any|error statusCode = s[0]; outputs[key] = statusCode is error ? statusCode.toString() : statusCode.toString(); } public function fetchOutput(string key) returns (anydata|error) { return outputs[key]; } function fetchHttpResponse(http:Response|http:PayloadType|error response) returns HttpResponseDetails { string responseMessage = ""; int statusCode = -1; if (response is http:Response) { statusCode = response.statusCode; var msg = response.getTextPayload(); if (msg is string) { responseMessage = msg; } else { responseMessage = "Invalid payload received"; } } else { responseMessage = "Error when calling the backend"; } HttpResponseDetails httpResponseDetails = { statusCode: statusCode, responseMessage: responseMessage }; return httpResponseDetails; }