├── examples ├── DM-mentions │ ├── .github │ │ └── README.md │ ├── Ballerina.toml │ ├── Direct message company mentions.md │ ├── main.bal │ └── Dependencies.toml ├── tweet-performance-tracker │ ├── .github │ │ └── README.md │ ├── Ballerina.toml │ ├── Tweet performance tracker.md │ ├── main.bal │ └── Dependencies.toml ├── README.md ├── build.sh └── build.gradle ├── ballerina ├── icon.png ├── Ballerina.toml ├── tests │ ├── README.md │ ├── tests.bal │ └── mock_service.bal ├── build.gradle ├── README.md ├── utils.bal ├── Dependencies.toml └── client.bal ├── docs ├── setup │ └── resources │ │ ├── set-up.png │ │ ├── authorize.png │ │ ├── get-keys.png │ │ └── twitter-developer-portal.png ├── license.txt └── spec │ └── sanitations.md ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties └── libs.versions.toml ├── .github ├── pull_request_template.md ├── CODEOWNERS └── workflows │ ├── daily-build.yml │ ├── release.yml │ ├── ci.yml │ ├── pull-request.yml │ ├── dev-stg-release.yml │ └── regenerate-connector.yml ├── .gitattributes ├── gradle.properties ├── .gitignore ├── settings.gradle ├── gradlew.bat ├── gradlew ├── LICENSE └── README.md /examples/DM-mentions/.github/README.md: -------------------------------------------------------------------------------- 1 | ../Direct message company mentions.md -------------------------------------------------------------------------------- /examples/tweet-performance-tracker/.github/README.md: -------------------------------------------------------------------------------- 1 | ../Tweet performance tracker.md -------------------------------------------------------------------------------- /ballerina/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ballerina-platform/module-ballerinax-twitter/HEAD/ballerina/icon.png -------------------------------------------------------------------------------- /docs/setup/resources/set-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ballerina-platform/module-ballerinax-twitter/HEAD/docs/setup/resources/set-up.png -------------------------------------------------------------------------------- /examples/DM-mentions/Ballerina.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | org = "wso2" 3 | name = "DM_mentions" 4 | version = "0.1.0" 5 | distribution = "2201.9.0" 6 | -------------------------------------------------------------------------------- /docs/setup/resources/authorize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ballerina-platform/module-ballerinax-twitter/HEAD/docs/setup/resources/authorize.png -------------------------------------------------------------------------------- /docs/setup/resources/get-keys.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ballerina-platform/module-ballerinax-twitter/HEAD/docs/setup/resources/get-keys.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ballerina-platform/module-ballerinax-twitter/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /examples/tweet-performance-tracker/Ballerina.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | org = "wso2" 3 | name = "tweet_performance_tracker" 4 | version = "0.1.0" 5 | distribution = "2201.9.0" 6 | -------------------------------------------------------------------------------- /docs/setup/resources/twitter-developer-portal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ballerina-platform/module-ballerinax-twitter/HEAD/docs/setup/resources/twitter-developer-portal.png -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | # This file was generated by the Gradle 'init' task. 2 | # https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format 3 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Purpose 2 | 3 | ## Examples 4 | 5 | ## Checklist 6 | - [ ] Linked to an issue 7 | - [ ] Updated the changelog 8 | - [ ] Added tests 9 | - [ ] Updated the spec 10 | - [ ] Checked native-image compatibility 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # 2 | # https://help.github.com/articles/dealing-with-line-endings/ 3 | # 4 | # Linux start script should use lf 5 | /gradlew text eol=lf 6 | 7 | # These are Windows script files and should use crlf 8 | *.bat text eol=crlf 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Lines starting with '#' are comments. 2 | # Each line is a file pattern followed by one or more owners. 3 | 4 | # See: https://help.github.com/articles/about-codeowners/ 5 | 6 | # These owners will be the default owners for everything in the repo. 7 | * @NipunaRanasinghe @shafreenAnfar 8 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.caching=true 2 | group=io.ballerina.lib 3 | version=5.0.0-SNAPSHOT 4 | 5 | checkstylePluginVersion=10.12.0 6 | spotbugsPluginVersion=5.0.14 7 | shadowJarPluginVersion=8.1.1 8 | downloadPluginVersion=5.4.0 9 | releasePluginVersion=2.8.0 10 | testngVersion=7.6.1 11 | eclipseLsp4jVersion=0.12.0 12 | ballerinaGradlePluginVersion=2.3.0 13 | ballerinaLangVersion=2201.12.2 14 | -------------------------------------------------------------------------------- /.github/workflows/daily-build.yml: -------------------------------------------------------------------------------- 1 | name: Daily build 2 | 3 | on: 4 | schedule: 5 | - cron: "30 2 * * *" 6 | 7 | jobs: 8 | call_workflow: 9 | name: Run Daily Build Workflow 10 | if: ${{ github.repository_owner == 'ballerina-platform' }} 11 | uses: ballerina-platform/ballerina-library/.github/workflows/daily-build-connector-template.yml@main 12 | secrets: inherit 13 | with: 14 | repo-name: module-ballerinax-twitter 15 | -------------------------------------------------------------------------------- /ballerina/Ballerina.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | distribution = "2201.12.0" 3 | org = "ballerinax" 4 | name = "twitter" 5 | version = "5.0.0" 6 | license = ["Apache-2.0"] 7 | authors = ["Ballerina"] 8 | keywords = ["Marketing/Social Media Accounts", "Cost/Freemium", "Vendor/Twitter", "Area/Social Media", "Type/Connector"] 9 | icon = "icon.png" 10 | repository = "https://github.com/ballerina-platform/module-ballerinax-twitter" 11 | 12 | [build-options] 13 | observabilityIncluded = true 14 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Publish Release 2 | 3 | on: 4 | workflow_dispatch: 5 | repository_dispatch: 6 | types: [ stdlib-release-pipeline ] 7 | 8 | jobs: 9 | call_workflow: 10 | name: Run Release Workflow 11 | if: ${{ github.repository_owner == 'ballerina-platform' }} 12 | uses: ballerina-platform/ballerina-library/.github/workflows/release-package-connector-template.yml@main 13 | secrets: inherit 14 | with: 15 | package-name: twitter 16 | package-org: ballerinax 17 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - 2201.[0-9]+.x 8 | repository_dispatch: 9 | types: check_connector_for_breaking_changes 10 | 11 | jobs: 12 | call_workflow: 13 | name: Run Connector Build Workflow 14 | if: ${{ github.repository_owner == 'ballerina-platform' }} 15 | uses: ballerina-platform/ballerina-library/.github/workflows/build-connector-template.yml@main 16 | secrets: inherit 17 | with: 18 | repo-name: module-ballerinax-twitter 19 | -------------------------------------------------------------------------------- /.github/workflows/pull-request.yml: -------------------------------------------------------------------------------- 1 | name: PR Build 2 | 3 | concurrency: 4 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} 5 | cancel-in-progress: true 6 | 7 | on: pull_request 8 | 9 | jobs: 10 | call_workflow: 11 | name: Run PR Build Workflow 12 | if: ${{ github.repository_owner == 'ballerina-platform' }} 13 | uses: ballerina-platform/ballerina-library/.github/workflows/pr-build-connector-template.yml@main 14 | secrets: inherit 15 | with: 16 | additional-test-flags: ${{ github.event.pull_request.head.repo.full_name != github.repository && '-x test' || ''}} 17 | -------------------------------------------------------------------------------- /.github/workflows/dev-stg-release.yml: -------------------------------------------------------------------------------- 1 | name: Publish to the Ballerina Dev\Stage Central 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | environment: 7 | type: choice 8 | description: Select Environment 9 | required: true 10 | options: 11 | - DEV CENTRAL 12 | - STAGE CENTRAL 13 | 14 | jobs: 15 | call_workflow: 16 | name: Run Dev\Stage Central Publish Workflow 17 | if: ${{ github.repository_owner == 'ballerina-platform' }} 18 | uses: ballerina-platform/ballerina-library/.github/workflows/dev-stage-central-publish-connector-template.yml@main 19 | secrets: inherit 20 | with: 21 | environment: ${{ github.event.inputs.environment }} 22 | -------------------------------------------------------------------------------- /docs/license.txt: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED FILE. DO NOT MODIFY. 2 | // This file is auto-generated by the Ballerina OpenAPI tool. 3 | 4 | // Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). 5 | // 6 | // WSO2 LLC. licenses this file to you under the Apache License, 7 | // Version 2.0 (the "License"); you may not use this file except 8 | // in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | *.balx 4 | # Log file 5 | *.log 6 | 7 | generated 8 | #Config file 9 | Config.toml 10 | 11 | # BlueJ files 12 | *.ctxt 13 | 14 | # Mobile Tools for Java (J2ME) 15 | .mtj.tmp/ 16 | 17 | # .DS_Store files 18 | *.DS_Store 19 | 20 | # Package Files # 21 | *.jar 22 | !gradle/wrapper/gradle-wrapper.jar 23 | *.war 24 | *.ear 25 | *.zip 26 | *.tar.gz 27 | *.rar 28 | *.deb 29 | 30 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 31 | hs_err_pid* 32 | 33 | # Ignore everything in this directory 34 | target 35 | .classpath 36 | .settings 37 | .project 38 | *.iml 39 | *.iws 40 | *.ipr 41 | .idea 42 | .m2 43 | .vscode/ 44 | # Ignore ballerina files 45 | accessToken.bal 46 | temp.bal.ballerina/ 47 | target/ 48 | .DS_Store 49 | *Ballerina.lock 50 | .ballerina 51 | 52 | # Ignore Gradle project-specific cache directory 53 | .gradle 54 | 55 | # Ignore Gradle build output directory 56 | build 57 | 58 | # Ignore Docker env file 59 | docker.env 60 | -------------------------------------------------------------------------------- /examples/DM-mentions/Direct message company mentions.md: -------------------------------------------------------------------------------- 1 | ## Customer Support tweets and Feedback Management 2 | 3 | This use case demonstrates how the Twitter API v2 can be utilized to enhance customer engagement by monitoring mentions of a company's support handle and providing timely responses through direct messages. The example involves a sequence of actions that leverage the Twitter API v2 to automate and streamline customer support efforts. 4 | 5 | ## Prerequisites 6 | 7 | ### 1. Setup Twitter developer account 8 | 9 | Refer to the [Setup guide](https://central.ballerina.io/ballerinax/twitter/latest#setup-guide) to obtain necessary credentials (client Id, client secret, tokens). 10 | 11 | ### 2. Configuration 12 | 13 | Create a `Config.toml` file in the example's root directory and, provide your Twitter account related configurations as follows: 14 | 15 | ```bash 16 | token = "" 17 | ``` 18 | 19 | ## Run the example 20 | 21 | Execute the following command to run the example: 22 | 23 | ```bash 24 | bal run 25 | ``` -------------------------------------------------------------------------------- /examples/tweet-performance-tracker/Tweet performance tracker.md: -------------------------------------------------------------------------------- 1 | ## Tweet performance analyzer 2 | 3 | This use case demonstrates how the Twitter API v2 can be utilized to analyze the performance of tweets posted by a user over the past month. The example involves a sequence of actions that leverage the Twitter API v2 to automate the retrieval of tweets and their performance metrics (likes, retweets, replies), and then create a performance report. 4 | 5 | ## Prerequisites 6 | 7 | ### 1. Setup Twitter developer account 8 | 9 | Refer to the [Setup guide](https://central.ballerina.io/ballerinax/twitter/latest#setup-guide) to obtain necessary credentials (client Id, client secret, tokens). 10 | 11 | ### 2. Configuration 12 | 13 | Create a `Config.toml` file in the example's root directory and, provide your Twitter account related configurations as follows: 14 | 15 | ```bash 16 | token = "" 17 | ``` 18 | 19 | ## Run the example 20 | 21 | Execute the following command to run the example: 22 | 23 | ```bash 24 | bal run 25 | ``` -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | The `twitter` connector provides practical examples illustrating usage in various scenarios. Explore these [examples](https://github.com/ballerina-platform/module-ballerinax-twitter/tree/main/examples), covering use cases like Direct message company mentions, and tweet performance tracker. 4 | 5 | 1. [Direct message company mentions](https://github.com/ballerina-platform/module-ballerinax-twitter/tree/main/examples/DM-mentions) - Integrate Twitter to send direct messages to users who mention the company in tweets. 6 | 7 | 2. [Tweet performance tracker](https://github.com/ballerina-platform/module-ballerinax-twitter/tree/main/examples/tweet-performance-tracker) - Analyze the performance of tweets posted by a user over the past month. 8 | 9 | 10 | ## Prerequisites 11 | 12 | 1. Generate Twitter credentials to authenticate the connector as described in the [Setup guide](https://central.ballerina.io/ballerinax/twitter/latest#setup-guide). 13 | 14 | 2. For each example, create a `Config.toml` file the related configuration. Here's an example of how your `Config.toml` file should look: 15 | 16 | ```toml 17 | token = "" 18 | ``` 19 | 20 | ## Running an Example 21 | 22 | Execute the following commands to build an example from the source: 23 | 24 | * To build an example: 25 | 26 | ```bash 27 | bal build 28 | ``` 29 | 30 | * To run an example: 31 | 32 | ```bash 33 | bal run 34 | ``` 35 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | * 4 | * The settings file is used to specify which projects to include in your build. 5 | * For more detailed information on multi-project builds, please refer to https://docs.gradle.org/8.3/userguide/building_swift_projects.html in the Gradle documentation. 6 | */ 7 | 8 | pluginManagement { 9 | plugins { 10 | id "com.github.spotbugs-base" version "${spotbugsPluginVersion}" 11 | id "com.github.johnrengelman.shadow" version "${shadowJarPluginVersion}" 12 | id "de.undercouch.download" version "${downloadPluginVersion}" 13 | id "net.researchgate.release" version "${releasePluginVersion}" 14 | id "io.ballerina.plugin" version "${ballerinaGradlePluginVersion}" 15 | } 16 | 17 | repositories { 18 | gradlePluginPortal() 19 | maven { 20 | url = 'https://maven.pkg.github.com/ballerina-platform/*' 21 | credentials { 22 | username System.getenv("packageUser") 23 | password System.getenv("packagePAT") 24 | } 25 | } 26 | } 27 | } 28 | 29 | plugins { 30 | id "com.gradle.enterprise" version "3.2" 31 | } 32 | 33 | rootProject.name = 'module-ballerinax-twitter' 34 | 35 | include ':twitter-ballerina' 36 | 37 | project(':twitter-ballerina').projectDir = file("ballerina") 38 | 39 | gradleEnterprise { 40 | buildScan { 41 | termsOfServiceUrl = 'https://gradle.com/terms-of-service' 42 | termsOfServiceAgree = 'yes' 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /docs/spec/sanitations.md: -------------------------------------------------------------------------------- 1 | _Authors_: @vish-mv @loshan20011 \ 2 | _Created_: 2024/03/26 \ 3 | _Updated_: 2024/03/26 \ 4 | _Edition_: Swan Lake 5 | 6 | # Sanitation for OpenAPI specification 7 | 8 | This document records the sanitation done on top of the official OpenAPI specification from Twitter (X). The OpenAPI specification is obtained from the [Twitter OpenAPI Documentation](https://api.twitter.com/2/openapi.json/). These changes are implemented to enhance the overall usability and readability of the generated client. 9 | 10 | 1. **Change the `url` property of the `servers` object**: 11 | - **Original**: `https://api.twitter.com` 12 | - **Updated**: `https://api.twitter.com/2` 13 | - **Reason**: This change is made to ensure that all API paths are relative to the versioned base URL (`/2`), which improves the consistency and usability of the APIs. 14 | 15 | 2. **Update API Paths**: 16 | - **Original**: Paths included the version prefix in each endpoint (e.g., `/2/compliance/jobs`). 17 | - **Updated**: Paths are modified to remove the version prefix from the endpoints, as it is now included in the base URL. For example: 18 | - **Original**: `/2/compliance/jobs` 19 | - **Updated**: `/compliance/jobs` 20 | - **Reason**: This modification simplifies the API paths, making them shorter and more readable. It also centralizes the versioning to the base URL, which is a common best practice. 21 | 22 | 23 | ## OpenAPI cli command 24 | 25 | The following command was used to generate the Ballerina client from the OpenAPI specification. The command should be executed from the repository root directory. 26 | 27 | ```bash 28 | bal openapi -i docs/spec/openapi.json --mode client --license docs/license.txt -o ballerina 29 | ``` 30 | Note: The license year is hardcoded to 2024, change if necessary. 31 | -------------------------------------------------------------------------------- /examples/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BAL_EXAMPLES_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 4 | BAL_CENTRAL_DIR="$HOME/.ballerina/repositories/central.ballerina.io" 5 | BAL_HOME_DIR="$BAL_EXAMPLES_DIR/../ballerina" 6 | 7 | set -e 8 | 9 | case "$1" in 10 | build) 11 | BAL_CMD="build" 12 | ;; 13 | run) 14 | BAL_CMD="run" 15 | ;; 16 | *) 17 | echo "Invalid command provided: '$1'. Please provide 'build' or 'run' as the command." 18 | exit 1 19 | ;; 20 | esac 21 | 22 | # Read Ballerina package name 23 | BAL_PACKAGE_NAME=$(awk -F'"' '/^name/ {print $2}' "$BAL_HOME_DIR/Ballerina.toml") 24 | 25 | # Push the package to the local repository 26 | cd "$BAL_HOME_DIR" && 27 | bal pack && 28 | bal push --repository=local 29 | 30 | # Remove the cache directories in the repositories 31 | cacheDirs=$(ls -d $BAL_CENTRAL_DIR/cache-* 2>/dev/null) || true 32 | for dir in "${cacheDirs[@]}"; do 33 | [ -d "$dir" ] && rm -r "$dir" 34 | done 35 | echo "Successfully cleaned the cache directories" 36 | 37 | # Create the package directory in the central repository, this will not be present if no modules are pulled 38 | mkdir -p "$BAL_CENTRAL_DIR/bala/ballerinax/$BAL_PACKAGE_NAME" 39 | 40 | # Update the central repository 41 | BAL_DESTINATION_DIR="$HOME/.ballerina/repositories/central.ballerina.io/bala/ballerinax/$BAL_PACKAGE_NAME" 42 | BAL_SOURCE_DIR="$HOME/.ballerina/repositories/local/bala/ballerinax/$BAL_PACKAGE_NAME" 43 | [ -d "$BAL_DESTINATION_DIR" ] && rm -r "$BAL_DESTINATION_DIR" 44 | [ -d "$BAL_SOURCE_DIR" ] && cp -r "$BAL_SOURCE_DIR" "$BAL_DESTINATION_DIR" 45 | echo "Successfully updated the local central repositories" 46 | 47 | echo "$BAL_DESTINATION_DIR" 48 | echo "$BAL_SOURCE_DIR" 49 | 50 | # Loop through examples in the examples directory 51 | cd "$BAL_EXAMPLES_DIR" 52 | for dir in $(find "$BAL_EXAMPLES_DIR" -type d -maxdepth 1 -mindepth 1); do 53 | # Skip the build directory 54 | if [[ "$dir" == *build ]]; then 55 | continue 56 | fi 57 | (cd "$dir" && bal "$BAL_CMD" --offline && cd ..); 58 | done 59 | 60 | # Remove generated JAR files 61 | find "$BAL_HOME_DIR" -maxdepth 1 -type f -name "*.jar" | while read -r JAR_FILE; do 62 | rm "$JAR_FILE" 63 | done 64 | -------------------------------------------------------------------------------- /.github/workflows/regenerate-connector.yml: -------------------------------------------------------------------------------- 1 | name: Regenerate OpenAPI Connector 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | openapi-url: 7 | description: "URL of the OpenAPI JSON" 8 | required: false 9 | type: string 10 | flatten-openapi: 11 | description: "Enable OpenAPI Flattening" 12 | required: false 13 | type: boolean 14 | default: true 15 | additional-flatten-flags: 16 | description: "Additional flags for OpenAPI Flattening" 17 | required: false 18 | type: string 19 | default: "" 20 | align-openapi: 21 | description: "Enable OpenAPI Alignment" 22 | required: false 23 | type: boolean 24 | default: true 25 | additional-align-flags: 26 | description: "Additional flags for OpenAPI Alignment" 27 | required: false 28 | type: string 29 | default: "" 30 | additional-generation-flags: 31 | description: "Additional flags for OpenAPI Generation" 32 | required: false 33 | type: string 34 | default: "" 35 | distribution-zip: 36 | description: "Distribution of the Ballerina version to be used" 37 | required: false 38 | type: string 39 | default: "" 40 | auto-merge: 41 | description: "Enable auto-merge of the PR" 42 | required: false 43 | type: boolean 44 | default: true 45 | ballerina-version: 46 | description: "Ballerina Language Version" 47 | required: false 48 | type: string 49 | default: "" 50 | 51 | jobs: 52 | call_workflow: 53 | name: Run Regenerate Connector Workflow 54 | if: ${{ github.repository_owner == 'ballerina-platform' }} 55 | uses: ballerina-platform/ballerina-library/.github/workflows/regenerate-connector-template.yml@main 56 | secrets: inherit 57 | with: 58 | openapi-url: ${{ inputs.openapi-url }} 59 | flatten-openapi: ${{ inputs.flatten-openapi }} 60 | additional-flatten-flags: ${{ inputs.additional-flatten-flags }} 61 | align-openapi: ${{ inputs.align-openapi }} 62 | additional-align-flags: ${{ inputs.additional-align-flags }} 63 | additional-generation-flags: ${{ inputs.additional-generation-flags }} 64 | distribution-zip: ${{ inputs.distribution-zip }} 65 | auto-merge: ${{ inputs.auto-merge }} 66 | ballerina-version: ${{ inputs.ballerina-version }} 67 | -------------------------------------------------------------------------------- /examples/DM-mentions/main.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 WSO2 LLC. (http://www.wso2.com). 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/io; 18 | import ballerinax/twitter; 19 | 20 | configurable string token = ?; 21 | final twitter:Client twitter = check new ({ 22 | auth: { 23 | token 24 | } 25 | }); 26 | 27 | public function main() returns error? { 28 | twitter:Get2TweetsSearchRecentResponse supportTweets = check twitter->/tweets/search/recent( 29 | query = "#ballerinaSupport", 30 | maxResults = 10 31 | ); 32 | twitter:Tweet[]? tweets = supportTweets.data; 33 | if tweets is () { 34 | io:println("No tweets found"); 35 | return; 36 | } 37 | 38 | twitter:TweetId[] tweetIds = []; 39 | foreach twitter:Tweet tweet in tweets { 40 | if tweet.id !is () { 41 | tweetIds.push(check tweet.id.ensureType(string)); 42 | } 43 | } 44 | 45 | twitter:UserId[] userIds = []; 46 | foreach twitter:TweetId tweetId in tweetIds { 47 | twitter:FindTweetByIdQueries searchQuery = { 48 | tweetFields: ["author_id"] 49 | }; 50 | twitter:Get2TweetsIdResponse tweetResponse = check twitter->/tweets/[tweetId](queries = searchQuery); 51 | if tweetResponse.data?.authorId !is () { 52 | userIds.push(check tweetResponse.data?.authorId.ensureType(string)); 53 | } 54 | } 55 | 56 | foreach twitter:UserId userId in userIds { 57 | twitter:CreateDmEventResponse dmResponse = check twitter->/dm_conversations/with/[userId]/messages.post( 58 | payload = { 59 | text: "Thank you for reaching us! We will reach you soon" 60 | } 61 | ); 62 | 63 | if dmResponse.errors is twitter:Problem[] { 64 | twitter:Problem[] problems = dmResponse.errors; 65 | foreach twitter:Problem problem in problems { 66 | io:println("Failed to send DM to user: ", userId); 67 | io:println("Error: ", problem.detail); 68 | } 69 | } else { 70 | io:println("User ID: ", userId); 71 | io:println("DM sent successfully"); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /examples/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). 3 | * 4 | * WSO2 LLC. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | 19 | import org.apache.tools.ant.taskdefs.condition.Os 20 | 21 | apply plugin: 'java' 22 | 23 | def graalvmFlag = "" 24 | 25 | task testExamples { 26 | if (project.hasProperty("balGraalVMTest")) { 27 | graalvmFlag = "--graalvm" 28 | } 29 | doLast { 30 | try { 31 | exec { 32 | workingDir project.projectDir 33 | println("Working dir: ${workingDir}") 34 | if (Os.isFamily(Os.FAMILY_WINDOWS)) { 35 | commandLine 'cmd', "/c", "chmod +x ./build.sh && ./build.sh run && exit %%ERRORLEVEL%%" 36 | } else { 37 | commandLine 'sh', "-c", "chmod +x ./build.sh && ./build.sh run" 38 | } 39 | } 40 | } catch (Exception e) { 41 | println("Example Build failed: " + e.message) 42 | throw e 43 | } 44 | } 45 | } 46 | 47 | task buildExamples { 48 | gradle.taskGraph.whenReady { graph -> 49 | if (graph.hasTask(":twitter-examples:test")) { 50 | buildExamples.enabled = false 51 | } else { 52 | testExamples.enabled = false 53 | } 54 | } 55 | doLast { 56 | try { 57 | exec { 58 | workingDir project.projectDir 59 | println("Working dir: ${workingDir}") 60 | if (Os.isFamily(Os.FAMILY_WINDOWS)) { 61 | commandLine 'cmd', "/c", "chmod +x ./build.sh && ./build.sh build && exit %%ERRORLEVEL%%" 62 | } else { 63 | commandLine 'sh', "-c", "chmod +x ./build.sh && ./build.sh build" 64 | } 65 | } 66 | } catch (Exception e) { 67 | println("Example Build failed: " + e.message) 68 | throw e 69 | } 70 | } 71 | } 72 | 73 | buildExamples.dependsOn ":twitter-ballerina:build" 74 | testExamples.dependsOn ":twitter-ballerina:build" 75 | 76 | // TODO: Enable the examples build once https://github.com/ballerina-platform/ballerina-library/issues/6135 is fixed 77 | // test.dependsOn testExamples 78 | // build.dependsOn buildExamples 79 | -------------------------------------------------------------------------------- /examples/tweet-performance-tracker/main.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 WSO2 LLC. (http://www.wso2.com). 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/io; 18 | import ballerina/lang.array; 19 | import ballerinax/twitter; 20 | 21 | configurable string token = ?; 22 | final string username = ""; 23 | final twitter:Client twitter = check new ({ 24 | auth: { 25 | token 26 | } 27 | }); 28 | 29 | public function main() returns error? { 30 | twitter:Get2UsersByUsernameUsernameResponse userResponse = check twitter->/users/'by/username/[username](); 31 | twitter:User? user = userResponse.data; 32 | if user is () { 33 | io:println("User not found"); 34 | return; 35 | } 36 | 37 | twitter:UserId userId = user.id; 38 | twitter:Get2UsersIdTweetsResponse tweetsResponse = check twitter->/users/[userId]/tweets({ 39 | start_time: "2024-06-01T00:00:00Z", 40 | end_time: "2024-07-01T00:00:00Z" 41 | }); 42 | twitter:Tweet[]? tweets = tweetsResponse.data; 43 | if tweets is () { 44 | io:println("No tweets for the user"); 45 | return; 46 | } 47 | 48 | twitter:FindTweetByIdQueries queries = { 49 | tweetFields: ["public_metrics"] 50 | }; 51 | twitter:Tweet[] performingTweets = []; 52 | foreach twitter:Tweet tweet in tweets { 53 | twitter:TweetId? tweetId = tweet.id; 54 | if tweetId is () { 55 | continue; 56 | } 57 | 58 | twitter:Get2TweetsIdResponse tweetResponse = check twitter->/tweets/[tweetId](queries = queries); 59 | twitter:Tweet? tweetData = tweetResponse.data; 60 | if tweetData !is () { 61 | tweet.publicMetrics = tweetData?.publicMetrics; 62 | performingTweets.push(tweet); 63 | } 64 | } 65 | 66 | twitter:Tweet[] sortedPerformingTweets = performingTweets.sort( 67 | array:DESCENDING, 68 | isolated function(twitter:Tweet t) returns int? => t.publicMetrics?.likeCount 69 | ); 70 | io:println("Top Tweets by ", username, " in the last month: "); 71 | foreach twitter:Tweet tweet in sortedPerformingTweets { 72 | io:println("Tweet: ", tweet.text); 73 | io:println("Likes: ", tweet.publicMetrics?.likeCount); 74 | io:println("Retweet Count: ", tweet.publicMetrics?.retweetCount); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /ballerina/tests/README.md: -------------------------------------------------------------------------------- 1 | # Running Tests 2 | 3 | ## Prerequisites 4 | You need a bearer token and a Access token from twittter(X) developer account. 5 | 6 | To do this, refer to [Ballerina Twitter Connector](https://github.com/ballerina-platform/module-ballerinax-twitter/blob/main/ballerina/Module.md). 7 | 8 | And You need Find Your User ID For run some of the tests. 9 | Via This Website you can find it [Twitter ID Finder](https://twiteridfinder.com/). 10 | 11 | # Running Tests 12 | 13 | There are two test environments for running the Twitter(X) connector tests. The default test environment is the mock server for Twitter API. The other test environment is the actual Twitter (X) API. 14 | 15 | You can run the tests in either of these environments and each has its own compatible set of tests. 16 | 17 | Test Groups | Environment 18 | -------------|--------------------------------------------------- 19 | mock_tests | Mock server for Twitter(X) API (Defualt Environment) 20 | live_tests | Twitter(X) API 21 | 22 | ## Running Tests in the Mock Server 23 | 24 | To execute the tests on the mock server, ensure that the `IS_LIVE_SERVER` environment variable is either set to `false` or unset before initiating the tests. 25 | 26 | This environment variable can be configured within the `Config.toml` file located in the tests directory or specified as an environmental variable. 27 | 28 | #### Using a Config.toml File 29 | 30 | Create a `Config.toml` file in the tests directory and the following content: 31 | 32 | ```toml 33 | isLiveServer = false 34 | ``` 35 | 36 | #### Using Environment Variables 37 | 38 | Alternatively, you can set your authentication credentials as environment variables: 39 | If you are using linux or mac, you can use following method: 40 | ```bash 41 | export IS_LIVE_SERVER=false 42 | ``` 43 | If you are using Windows you can use following method: 44 | ```bash 45 | setx IS_LIVE_SERVER false 46 | ``` 47 | Then, run the following command to run the tests: 48 | 49 | ```bash 50 | ./gradlew clean test 51 | ``` 52 | 53 | ## Running Tests Against Twitter(X) Live API 54 | 55 | #### Using a Config.toml File 56 | 57 | Create a `Config.toml` file in the tests directory and add your authentication credentials a 58 | 59 | ```toml 60 | isLiveServer = true 61 | token = "" 62 | userId = "" 63 | ``` 64 | 65 | #### Using Environment Variables 66 | 67 | Alternatively, you can set your authentication credentials as environment variables: 68 | If you are using linux or mac, you can use following method: 69 | ```bash 70 | export IS_LIVE_SERVER=true 71 | export TWITTER_TOKEN ="" 72 | export TWITTER_USER_ID ="" 73 | ``` 74 | 75 | If you are using Windows you can use following method: 76 | ```bash 77 | setx IS_LIVE_SERVER true 78 | setx TWITTER_TOKEN 79 | setx TWITTER_USER_ID 80 | ``` 81 | Then, run the following command to run the tests: 82 | 83 | ```bash 84 | ./gradlew clean test 85 | ``` 86 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 1>&2 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 48 | echo. 1>&2 49 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 50 | echo location of your Java installation. 1>&2 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 1>&2 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 62 | echo. 1>&2 63 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 64 | echo location of your Java installation. 1>&2 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /ballerina/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). 3 | * 4 | * WSO2 LLC. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | 19 | import org.apache.tools.ant.taskdefs.condition.Os 20 | 21 | plugins{ 22 | id 'io.ballerina.plugin' 23 | } 24 | 25 | description = 'Twitter - Ballerina' 26 | 27 | def packageName = "twitter" 28 | def packageOrg = "ballerinax" 29 | def tomlVersion = stripBallerinaExtensionVersion("${project.version}") 30 | def ballerinaTomlFilePlaceHolder = new File("${project.rootDir}/build-config/resources/Ballerina.toml") 31 | def ballerinaTomlFile = new File("$project.projectDir/Ballerina.toml") 32 | 33 | def stripBallerinaExtensionVersion(String extVersion) { 34 | if (extVersion.matches(project.ext.timestampedVersionRegex)) { 35 | def splitVersion = extVersion.split('-') 36 | if (splitVersion.length > 3) { 37 | def strippedValues = splitVersion[0..-4] 38 | return strippedValues.join('-') 39 | } else { 40 | return extVersion 41 | } 42 | } else { 43 | return extVersion.replace("${project.ext.snapshotVersion}", "") 44 | } 45 | } 46 | 47 | ballerina { 48 | packageOrganization = packageOrg 49 | module = packageName 50 | testCoverageParam = "--code-coverage --coverage-format=xml" 51 | isConnector = true 52 | platform = "any" 53 | } 54 | 55 | task updateTomlFiles { 56 | doLast { 57 | def newBallerinaToml = ballerinaTomlFilePlaceHolder.text.replace("@project.version@", project.version) 58 | newBallerinaToml = newBallerinaToml.replace("@toml.version@", tomlVersion) 59 | ballerinaTomlFile.text = newBallerinaToml 60 | } 61 | } 62 | 63 | task commitTomlFiles { 64 | doLast { 65 | project.exec { 66 | ignoreExitValue true 67 | if (Os.isFamily(Os.FAMILY_WINDOWS)) { 68 | commandLine 'cmd', '/c', "git commit -m \"[Automated] Update the toml files\" Ballerina.toml Dependencies.toml" 69 | } else { 70 | commandLine 'sh', '-c', "git commit -m '[Automated] Update the toml files' Ballerina.toml Dependencies.toml" 71 | } 72 | } 73 | } 74 | } 75 | 76 | publishing { 77 | publications { 78 | maven(MavenPublication) { 79 | artifact source: createArtifactZip, extension: 'zip' 80 | } 81 | } 82 | repositories { 83 | maven { 84 | name = "GitHubPackages" 85 | url = uri("https://maven.pkg.github.com/ballerina-platform/module-${packageOrg}-${packageName}") 86 | credentials { 87 | username = System.getenv("publishUser") 88 | password = System.getenv("publishPAT") 89 | } 90 | } 91 | } 92 | } 93 | 94 | clean { 95 | delete 'build' 96 | } 97 | 98 | build.dependsOn "generatePomFileForMavenPublication" 99 | publishToMavenLocal.dependsOn build 100 | publish.dependsOn build 101 | -------------------------------------------------------------------------------- /ballerina/tests/tests.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/os; 18 | import ballerina/test; 19 | import ballerina/time; 20 | 21 | configurable boolean isLiveServer = os:getEnv("IS_LIVE_SERVER") == "true"; 22 | configurable string userId = isLiveServer ? os:getEnv("TWITTER_USER_ID") : "test"; 23 | configurable string token = isLiveServer ? os:getEnv("TWITTER_TOKEN") : "test"; 24 | configurable string serviceUrl = isLiveServer ? "https://api.twitter.com/2" : "http://localhost:9090"; 25 | 26 | ConnectionConfig config = {auth: {token}}; 27 | final Client twitter = check new Client(config, serviceUrl); 28 | 29 | final string testUserId = "15594932"; 30 | final string testPostId = "1808153657558311048"; 31 | 32 | @test:Config { 33 | groups: ["live_tests", "mock_tests"] 34 | } 35 | isolated function testPostTweet() returns error? { 36 | time:Utc utc = time:utcNow(); 37 | string tweetText = "Twitter Test at " + utc.toString(); 38 | TweetCreateResponse response = check twitter->/tweets.post(payload = { 39 | text: tweetText 40 | }); 41 | test:assertTrue(response?.data !is ()); 42 | test:assertTrue(response?.errors is ()); 43 | } 44 | 45 | @test:Config { 46 | groups: ["live_tests", "mock_tests"] 47 | } 48 | isolated function testgetUserIdByUseName() returns error? { 49 | Get2UsersByUsernameUsernameResponse response = check twitter->/users/'by/username/["KumarSanga2"]; 50 | test:assertTrue(response?.data !is ()); 51 | test:assertTrue(response?.errors is ()); 52 | } 53 | 54 | @test:Config { 55 | groups: ["live_tests", "mock_tests"] 56 | } 57 | isolated function testUserLikeAPost() returns error? { 58 | UsersLikesCreateResponse response = check twitter->/users/[userId]/likes.post( 59 | payload = { 60 | tweetId: testPostId 61 | } 62 | ); 63 | test:assertTrue(response?.data !is ()); 64 | test:assertTrue(response?.errors is ()); 65 | } 66 | 67 | @test:Config { 68 | groups: ["live_tests", "mock_tests"] 69 | } 70 | isolated function testUserUnlikeAPost() returns error? { 71 | UsersLikesDeleteResponse response = check twitter->/users/[userId]/likes/[testPostId].delete(); 72 | test:assertTrue(response?.data !is ()); 73 | test:assertTrue(response?.errors is ()); 74 | } 75 | 76 | @test:Config { 77 | groups: ["live_tests", "mock_tests"] 78 | } 79 | isolated function testPostLookup() returns error? { 80 | Get2TweetsIdResponse response = check twitter->/tweets/[testPostId](); 81 | test:assertTrue(response?.data !is ()); 82 | test:assertTrue(response?.errors is ()); 83 | } 84 | 85 | @test:Config { 86 | groups: ["live_tests", "mock_tests"] 87 | } 88 | isolated function testBookmarkPost() returns error? { 89 | BookmarkMutationResponse response = check twitter->/users/[userId]/bookmarks.post( 90 | payload = {tweetId: testPostId} 91 | ); 92 | test:assertTrue(response?.data !is ()); 93 | test:assertTrue(response?.errors is ()); 94 | } 95 | 96 | @test:Config { 97 | groups: ["live_tests", "mock_tests"] 98 | } 99 | isolated function testBookmarkDelete() returns error? { 100 | BookmarkMutationResponse response = check twitter->/users/[userId]/bookmarks/[testPostId].delete(); 101 | test:assertTrue(response?.data !is ()); 102 | test:assertTrue(response?.errors is ()); 103 | } 104 | 105 | @test:Config { 106 | groups: ["live_tests", "mock_tests"] 107 | } 108 | isolated function testRetweet() returns error? { 109 | UsersRetweetsCreateResponse response = check twitter->/users/[userId]/retweets.post( 110 | payload = {tweetId: testPostId} 111 | ); 112 | test:assertTrue(response?.data !is ()); 113 | test:assertTrue(response?.errors is ()); 114 | } 115 | 116 | @test:Config { 117 | groups: ["live_tests", "mock_tests"] 118 | } 119 | isolated function testDeleteRetweet() returns error? { 120 | UsersRetweetsDeleteResponse response = check twitter->/users/[userId]/retweets/[testPostId].delete(); 121 | test:assertTrue(response?.data !is ()); 122 | test:assertTrue(response?.errors is ()); 123 | } 124 | 125 | @test:Config { 126 | groups: ["live_tests", "mock_tests"] 127 | } 128 | isolated function testFollowSpecificUser() returns error? { 129 | UsersFollowingCreateResponse response = check twitter->/users/[userId]/following.post( 130 | payload = { 131 | targetUserId: testUserId 132 | } 133 | ); 134 | test:assertTrue(response?.data !is ()); 135 | test:assertTrue(response?.errors is ()); 136 | } 137 | 138 | @test:Config { 139 | groups: ["live_tests", "mock_tests"] 140 | } 141 | isolated function testUnfollowSpecificUser() returns error? { 142 | UsersFollowingDeleteResponse response = check twitter->/users/[userId]/following/[testUserId].delete(); 143 | test:assertTrue(response?.data !is ()); 144 | test:assertTrue(response?.errors is ()); 145 | } 146 | 147 | @test:Config { 148 | groups: ["live_tests", "mock_tests"] 149 | } 150 | isolated function muteSpecificUser() returns error? { 151 | MuteUserMutationResponse response = check twitter->/users/[userId]/muting.post( 152 | payload = { 153 | targetUserId: testUserId 154 | } 155 | ); 156 | test:assertTrue(response?.data !is ()); 157 | test:assertTrue(response?.errors is ()); 158 | } 159 | 160 | @test:Config { 161 | groups: ["live_tests", "mock_tests"] 162 | } 163 | 164 | isolated function unmuteSpecificUser() returns error? { 165 | MuteUserMutationResponse response = check twitter->/users/[userId]/muting/[testUserId].delete(); 166 | test:assertTrue(response?.data !is ()); 167 | test:assertTrue(response?.errors is ()); 168 | } 169 | 170 | @test:Config { 171 | groups: ["live_tests", "mock_tests"] 172 | } 173 | isolated function findSpecificUser() returns error? { 174 | Get2UsersResponse response = check twitter->/users(ids = [testUserId]); 175 | test:assertTrue(response?.data !is ()); 176 | test:assertTrue(response?.errors is ()); 177 | } 178 | -------------------------------------------------------------------------------- /examples/DM-mentions/Dependencies.toml: -------------------------------------------------------------------------------- 1 | # AUTO-GENERATED FILE. DO NOT MODIFY. 2 | 3 | # This file is auto-generated by Ballerina for managing dependency versions. 4 | # It should not be modified by hand. 5 | 6 | [ballerina] 7 | dependencies-toml-version = "2" 8 | distribution-version = "2201.12.2" 9 | 10 | [[package]] 11 | org = "ballerina" 12 | name = "auth" 13 | version = "2.14.0" 14 | dependencies = [ 15 | {org = "ballerina", name = "crypto"}, 16 | {org = "ballerina", name = "jballerina.java"}, 17 | {org = "ballerina", name = "lang.array"}, 18 | {org = "ballerina", name = "lang.string"}, 19 | {org = "ballerina", name = "log"} 20 | ] 21 | 22 | [[package]] 23 | org = "ballerina" 24 | name = "cache" 25 | version = "3.10.0" 26 | dependencies = [ 27 | {org = "ballerina", name = "constraint"}, 28 | {org = "ballerina", name = "jballerina.java"}, 29 | {org = "ballerina", name = "task"}, 30 | {org = "ballerina", name = "time"} 31 | ] 32 | 33 | [[package]] 34 | org = "ballerina" 35 | name = "constraint" 36 | version = "1.7.0" 37 | dependencies = [ 38 | {org = "ballerina", name = "jballerina.java"} 39 | ] 40 | 41 | [[package]] 42 | org = "ballerina" 43 | name = "crypto" 44 | version = "2.9.0" 45 | dependencies = [ 46 | {org = "ballerina", name = "jballerina.java"}, 47 | {org = "ballerina", name = "time"} 48 | ] 49 | 50 | [[package]] 51 | org = "ballerina" 52 | name = "data.jsondata" 53 | version = "1.1.0" 54 | dependencies = [ 55 | {org = "ballerina", name = "jballerina.java"}, 56 | {org = "ballerina", name = "lang.object"} 57 | ] 58 | 59 | [[package]] 60 | org = "ballerina" 61 | name = "file" 62 | version = "1.12.0" 63 | dependencies = [ 64 | {org = "ballerina", name = "io"}, 65 | {org = "ballerina", name = "jballerina.java"}, 66 | {org = "ballerina", name = "os"}, 67 | {org = "ballerina", name = "time"} 68 | ] 69 | 70 | [[package]] 71 | org = "ballerina" 72 | name = "http" 73 | version = "2.14.0" 74 | dependencies = [ 75 | {org = "ballerina", name = "auth"}, 76 | {org = "ballerina", name = "cache"}, 77 | {org = "ballerina", name = "constraint"}, 78 | {org = "ballerina", name = "crypto"}, 79 | {org = "ballerina", name = "data.jsondata"}, 80 | {org = "ballerina", name = "file"}, 81 | {org = "ballerina", name = "io"}, 82 | {org = "ballerina", name = "jballerina.java"}, 83 | {org = "ballerina", name = "jwt"}, 84 | {org = "ballerina", name = "lang.array"}, 85 | {org = "ballerina", name = "lang.decimal"}, 86 | {org = "ballerina", name = "lang.int"}, 87 | {org = "ballerina", name = "lang.regexp"}, 88 | {org = "ballerina", name = "lang.runtime"}, 89 | {org = "ballerina", name = "lang.string"}, 90 | {org = "ballerina", name = "lang.value"}, 91 | {org = "ballerina", name = "log"}, 92 | {org = "ballerina", name = "mime"}, 93 | {org = "ballerina", name = "oauth2"}, 94 | {org = "ballerina", name = "observe"}, 95 | {org = "ballerina", name = "time"}, 96 | {org = "ballerina", name = "url"} 97 | ] 98 | 99 | [[package]] 100 | org = "ballerina" 101 | name = "io" 102 | version = "1.8.0" 103 | dependencies = [ 104 | {org = "ballerina", name = "jballerina.java"}, 105 | {org = "ballerina", name = "lang.value"} 106 | ] 107 | modules = [ 108 | {org = "ballerina", packageName = "io", moduleName = "io"} 109 | ] 110 | 111 | [[package]] 112 | org = "ballerina" 113 | name = "jballerina.java" 114 | version = "0.0.0" 115 | 116 | [[package]] 117 | org = "ballerina" 118 | name = "jwt" 119 | version = "2.15.0" 120 | dependencies = [ 121 | {org = "ballerina", name = "cache"}, 122 | {org = "ballerina", name = "crypto"}, 123 | {org = "ballerina", name = "io"}, 124 | {org = "ballerina", name = "jballerina.java"}, 125 | {org = "ballerina", name = "lang.int"}, 126 | {org = "ballerina", name = "lang.string"}, 127 | {org = "ballerina", name = "log"}, 128 | {org = "ballerina", name = "time"} 129 | ] 130 | 131 | [[package]] 132 | org = "ballerina" 133 | name = "lang.__internal" 134 | version = "0.0.0" 135 | dependencies = [ 136 | {org = "ballerina", name = "jballerina.java"}, 137 | {org = "ballerina", name = "lang.object"} 138 | ] 139 | 140 | [[package]] 141 | org = "ballerina" 142 | name = "lang.array" 143 | version = "0.0.0" 144 | dependencies = [ 145 | {org = "ballerina", name = "jballerina.java"}, 146 | {org = "ballerina", name = "lang.__internal"} 147 | ] 148 | 149 | [[package]] 150 | org = "ballerina" 151 | name = "lang.decimal" 152 | version = "0.0.0" 153 | dependencies = [ 154 | {org = "ballerina", name = "jballerina.java"} 155 | ] 156 | 157 | [[package]] 158 | org = "ballerina" 159 | name = "lang.int" 160 | version = "0.0.0" 161 | dependencies = [ 162 | {org = "ballerina", name = "jballerina.java"}, 163 | {org = "ballerina", name = "lang.__internal"}, 164 | {org = "ballerina", name = "lang.object"} 165 | ] 166 | 167 | [[package]] 168 | org = "ballerina" 169 | name = "lang.object" 170 | version = "0.0.0" 171 | 172 | [[package]] 173 | org = "ballerina" 174 | name = "lang.regexp" 175 | version = "0.0.0" 176 | dependencies = [ 177 | {org = "ballerina", name = "jballerina.java"} 178 | ] 179 | 180 | [[package]] 181 | org = "ballerina" 182 | name = "lang.runtime" 183 | version = "0.0.0" 184 | dependencies = [ 185 | {org = "ballerina", name = "jballerina.java"} 186 | ] 187 | 188 | [[package]] 189 | org = "ballerina" 190 | name = "lang.string" 191 | version = "0.0.0" 192 | dependencies = [ 193 | {org = "ballerina", name = "jballerina.java"}, 194 | {org = "ballerina", name = "lang.regexp"} 195 | ] 196 | 197 | [[package]] 198 | org = "ballerina" 199 | name = "lang.value" 200 | version = "0.0.0" 201 | dependencies = [ 202 | {org = "ballerina", name = "jballerina.java"} 203 | ] 204 | 205 | [[package]] 206 | org = "ballerina" 207 | name = "log" 208 | version = "2.12.0" 209 | dependencies = [ 210 | {org = "ballerina", name = "io"}, 211 | {org = "ballerina", name = "jballerina.java"}, 212 | {org = "ballerina", name = "lang.value"}, 213 | {org = "ballerina", name = "observe"} 214 | ] 215 | 216 | [[package]] 217 | org = "ballerina" 218 | name = "mime" 219 | version = "2.12.0" 220 | dependencies = [ 221 | {org = "ballerina", name = "io"}, 222 | {org = "ballerina", name = "jballerina.java"}, 223 | {org = "ballerina", name = "lang.int"}, 224 | {org = "ballerina", name = "log"} 225 | ] 226 | 227 | [[package]] 228 | org = "ballerina" 229 | name = "oauth2" 230 | version = "2.14.0" 231 | dependencies = [ 232 | {org = "ballerina", name = "cache"}, 233 | {org = "ballerina", name = "crypto"}, 234 | {org = "ballerina", name = "jballerina.java"}, 235 | {org = "ballerina", name = "log"}, 236 | {org = "ballerina", name = "time"}, 237 | {org = "ballerina", name = "url"} 238 | ] 239 | 240 | [[package]] 241 | org = "ballerina" 242 | name = "observe" 243 | version = "1.5.0" 244 | dependencies = [ 245 | {org = "ballerina", name = "jballerina.java"} 246 | ] 247 | 248 | [[package]] 249 | org = "ballerina" 250 | name = "os" 251 | version = "1.10.0" 252 | dependencies = [ 253 | {org = "ballerina", name = "io"}, 254 | {org = "ballerina", name = "jballerina.java"} 255 | ] 256 | 257 | [[package]] 258 | org = "ballerina" 259 | name = "task" 260 | version = "2.7.0" 261 | dependencies = [ 262 | {org = "ballerina", name = "jballerina.java"}, 263 | {org = "ballerina", name = "time"} 264 | ] 265 | 266 | [[package]] 267 | org = "ballerina" 268 | name = "time" 269 | version = "2.7.0" 270 | dependencies = [ 271 | {org = "ballerina", name = "jballerina.java"} 272 | ] 273 | 274 | [[package]] 275 | org = "ballerina" 276 | name = "url" 277 | version = "2.6.0" 278 | dependencies = [ 279 | {org = "ballerina", name = "jballerina.java"} 280 | ] 281 | 282 | [[package]] 283 | org = "ballerinai" 284 | name = "observe" 285 | version = "0.0.0" 286 | dependencies = [ 287 | {org = "ballerina", name = "jballerina.java"}, 288 | {org = "ballerina", name = "observe"} 289 | ] 290 | 291 | [[package]] 292 | org = "ballerinax" 293 | name = "twitter" 294 | version = "5.0.0" 295 | dependencies = [ 296 | {org = "ballerina", name = "constraint"}, 297 | {org = "ballerina", name = "data.jsondata"}, 298 | {org = "ballerina", name = "http"}, 299 | {org = "ballerina", name = "log"}, 300 | {org = "ballerina", name = "os"}, 301 | {org = "ballerina", name = "time"}, 302 | {org = "ballerina", name = "url"}, 303 | {org = "ballerinai", name = "observe"} 304 | ] 305 | modules = [ 306 | {org = "ballerinax", packageName = "twitter", moduleName = "twitter"} 307 | ] 308 | 309 | [[package]] 310 | org = "wso2" 311 | name = "DM_mentions" 312 | version = "0.1.0" 313 | dependencies = [ 314 | {org = "ballerina", name = "io"}, 315 | {org = "ballerinax", name = "twitter"} 316 | ] 317 | modules = [ 318 | {org = "wso2", packageName = "DM_mentions", moduleName = "DM_mentions"} 319 | ] 320 | 321 | -------------------------------------------------------------------------------- /examples/tweet-performance-tracker/Dependencies.toml: -------------------------------------------------------------------------------- 1 | # AUTO-GENERATED FILE. DO NOT MODIFY. 2 | 3 | # This file is auto-generated by Ballerina for managing dependency versions. 4 | # It should not be modified by hand. 5 | 6 | [ballerina] 7 | dependencies-toml-version = "2" 8 | distribution-version = "2201.12.2" 9 | 10 | [[package]] 11 | org = "ballerina" 12 | name = "auth" 13 | version = "2.14.0" 14 | dependencies = [ 15 | {org = "ballerina", name = "crypto"}, 16 | {org = "ballerina", name = "jballerina.java"}, 17 | {org = "ballerina", name = "lang.array"}, 18 | {org = "ballerina", name = "lang.string"}, 19 | {org = "ballerina", name = "log"} 20 | ] 21 | 22 | [[package]] 23 | org = "ballerina" 24 | name = "cache" 25 | version = "3.10.0" 26 | dependencies = [ 27 | {org = "ballerina", name = "constraint"}, 28 | {org = "ballerina", name = "jballerina.java"}, 29 | {org = "ballerina", name = "task"}, 30 | {org = "ballerina", name = "time"} 31 | ] 32 | 33 | [[package]] 34 | org = "ballerina" 35 | name = "constraint" 36 | version = "1.7.0" 37 | dependencies = [ 38 | {org = "ballerina", name = "jballerina.java"} 39 | ] 40 | 41 | [[package]] 42 | org = "ballerina" 43 | name = "crypto" 44 | version = "2.9.0" 45 | dependencies = [ 46 | {org = "ballerina", name = "jballerina.java"}, 47 | {org = "ballerina", name = "time"} 48 | ] 49 | 50 | [[package]] 51 | org = "ballerina" 52 | name = "data.jsondata" 53 | version = "1.1.0" 54 | dependencies = [ 55 | {org = "ballerina", name = "jballerina.java"}, 56 | {org = "ballerina", name = "lang.object"} 57 | ] 58 | 59 | [[package]] 60 | org = "ballerina" 61 | name = "file" 62 | version = "1.12.0" 63 | dependencies = [ 64 | {org = "ballerina", name = "io"}, 65 | {org = "ballerina", name = "jballerina.java"}, 66 | {org = "ballerina", name = "os"}, 67 | {org = "ballerina", name = "time"} 68 | ] 69 | 70 | [[package]] 71 | org = "ballerina" 72 | name = "http" 73 | version = "2.14.0" 74 | dependencies = [ 75 | {org = "ballerina", name = "auth"}, 76 | {org = "ballerina", name = "cache"}, 77 | {org = "ballerina", name = "constraint"}, 78 | {org = "ballerina", name = "crypto"}, 79 | {org = "ballerina", name = "data.jsondata"}, 80 | {org = "ballerina", name = "file"}, 81 | {org = "ballerina", name = "io"}, 82 | {org = "ballerina", name = "jballerina.java"}, 83 | {org = "ballerina", name = "jwt"}, 84 | {org = "ballerina", name = "lang.array"}, 85 | {org = "ballerina", name = "lang.decimal"}, 86 | {org = "ballerina", name = "lang.int"}, 87 | {org = "ballerina", name = "lang.regexp"}, 88 | {org = "ballerina", name = "lang.runtime"}, 89 | {org = "ballerina", name = "lang.string"}, 90 | {org = "ballerina", name = "lang.value"}, 91 | {org = "ballerina", name = "log"}, 92 | {org = "ballerina", name = "mime"}, 93 | {org = "ballerina", name = "oauth2"}, 94 | {org = "ballerina", name = "observe"}, 95 | {org = "ballerina", name = "time"}, 96 | {org = "ballerina", name = "url"} 97 | ] 98 | 99 | [[package]] 100 | org = "ballerina" 101 | name = "io" 102 | version = "1.8.0" 103 | dependencies = [ 104 | {org = "ballerina", name = "jballerina.java"}, 105 | {org = "ballerina", name = "lang.value"} 106 | ] 107 | modules = [ 108 | {org = "ballerina", packageName = "io", moduleName = "io"} 109 | ] 110 | 111 | [[package]] 112 | org = "ballerina" 113 | name = "jballerina.java" 114 | version = "0.0.0" 115 | 116 | [[package]] 117 | org = "ballerina" 118 | name = "jwt" 119 | version = "2.15.0" 120 | dependencies = [ 121 | {org = "ballerina", name = "cache"}, 122 | {org = "ballerina", name = "crypto"}, 123 | {org = "ballerina", name = "io"}, 124 | {org = "ballerina", name = "jballerina.java"}, 125 | {org = "ballerina", name = "lang.int"}, 126 | {org = "ballerina", name = "lang.string"}, 127 | {org = "ballerina", name = "log"}, 128 | {org = "ballerina", name = "time"} 129 | ] 130 | 131 | [[package]] 132 | org = "ballerina" 133 | name = "lang.__internal" 134 | version = "0.0.0" 135 | dependencies = [ 136 | {org = "ballerina", name = "jballerina.java"}, 137 | {org = "ballerina", name = "lang.object"} 138 | ] 139 | 140 | [[package]] 141 | org = "ballerina" 142 | name = "lang.array" 143 | version = "0.0.0" 144 | dependencies = [ 145 | {org = "ballerina", name = "jballerina.java"}, 146 | {org = "ballerina", name = "lang.__internal"} 147 | ] 148 | modules = [ 149 | {org = "ballerina", packageName = "lang.array", moduleName = "lang.array"} 150 | ] 151 | 152 | [[package]] 153 | org = "ballerina" 154 | name = "lang.decimal" 155 | version = "0.0.0" 156 | dependencies = [ 157 | {org = "ballerina", name = "jballerina.java"} 158 | ] 159 | 160 | [[package]] 161 | org = "ballerina" 162 | name = "lang.int" 163 | version = "0.0.0" 164 | dependencies = [ 165 | {org = "ballerina", name = "jballerina.java"}, 166 | {org = "ballerina", name = "lang.__internal"}, 167 | {org = "ballerina", name = "lang.object"} 168 | ] 169 | 170 | [[package]] 171 | org = "ballerina" 172 | name = "lang.object" 173 | version = "0.0.0" 174 | 175 | [[package]] 176 | org = "ballerina" 177 | name = "lang.regexp" 178 | version = "0.0.0" 179 | dependencies = [ 180 | {org = "ballerina", name = "jballerina.java"} 181 | ] 182 | 183 | [[package]] 184 | org = "ballerina" 185 | name = "lang.runtime" 186 | version = "0.0.0" 187 | dependencies = [ 188 | {org = "ballerina", name = "jballerina.java"} 189 | ] 190 | 191 | [[package]] 192 | org = "ballerina" 193 | name = "lang.string" 194 | version = "0.0.0" 195 | dependencies = [ 196 | {org = "ballerina", name = "jballerina.java"}, 197 | {org = "ballerina", name = "lang.regexp"} 198 | ] 199 | 200 | [[package]] 201 | org = "ballerina" 202 | name = "lang.value" 203 | version = "0.0.0" 204 | dependencies = [ 205 | {org = "ballerina", name = "jballerina.java"} 206 | ] 207 | 208 | [[package]] 209 | org = "ballerina" 210 | name = "log" 211 | version = "2.12.0" 212 | dependencies = [ 213 | {org = "ballerina", name = "io"}, 214 | {org = "ballerina", name = "jballerina.java"}, 215 | {org = "ballerina", name = "lang.value"}, 216 | {org = "ballerina", name = "observe"} 217 | ] 218 | 219 | [[package]] 220 | org = "ballerina" 221 | name = "mime" 222 | version = "2.12.0" 223 | dependencies = [ 224 | {org = "ballerina", name = "io"}, 225 | {org = "ballerina", name = "jballerina.java"}, 226 | {org = "ballerina", name = "lang.int"}, 227 | {org = "ballerina", name = "log"} 228 | ] 229 | 230 | [[package]] 231 | org = "ballerina" 232 | name = "oauth2" 233 | version = "2.14.0" 234 | dependencies = [ 235 | {org = "ballerina", name = "cache"}, 236 | {org = "ballerina", name = "crypto"}, 237 | {org = "ballerina", name = "jballerina.java"}, 238 | {org = "ballerina", name = "log"}, 239 | {org = "ballerina", name = "time"}, 240 | {org = "ballerina", name = "url"} 241 | ] 242 | 243 | [[package]] 244 | org = "ballerina" 245 | name = "observe" 246 | version = "1.5.0" 247 | dependencies = [ 248 | {org = "ballerina", name = "jballerina.java"} 249 | ] 250 | 251 | [[package]] 252 | org = "ballerina" 253 | name = "os" 254 | version = "1.10.0" 255 | dependencies = [ 256 | {org = "ballerina", name = "io"}, 257 | {org = "ballerina", name = "jballerina.java"} 258 | ] 259 | 260 | [[package]] 261 | org = "ballerina" 262 | name = "task" 263 | version = "2.7.0" 264 | dependencies = [ 265 | {org = "ballerina", name = "jballerina.java"}, 266 | {org = "ballerina", name = "time"} 267 | ] 268 | 269 | [[package]] 270 | org = "ballerina" 271 | name = "time" 272 | version = "2.7.0" 273 | dependencies = [ 274 | {org = "ballerina", name = "jballerina.java"} 275 | ] 276 | 277 | [[package]] 278 | org = "ballerina" 279 | name = "url" 280 | version = "2.6.0" 281 | dependencies = [ 282 | {org = "ballerina", name = "jballerina.java"} 283 | ] 284 | 285 | [[package]] 286 | org = "ballerinai" 287 | name = "observe" 288 | version = "0.0.0" 289 | dependencies = [ 290 | {org = "ballerina", name = "jballerina.java"}, 291 | {org = "ballerina", name = "observe"} 292 | ] 293 | 294 | [[package]] 295 | org = "ballerinax" 296 | name = "twitter" 297 | version = "5.0.0" 298 | dependencies = [ 299 | {org = "ballerina", name = "constraint"}, 300 | {org = "ballerina", name = "data.jsondata"}, 301 | {org = "ballerina", name = "http"}, 302 | {org = "ballerina", name = "log"}, 303 | {org = "ballerina", name = "os"}, 304 | {org = "ballerina", name = "time"}, 305 | {org = "ballerina", name = "url"}, 306 | {org = "ballerinai", name = "observe"} 307 | ] 308 | modules = [ 309 | {org = "ballerinax", packageName = "twitter", moduleName = "twitter"} 310 | ] 311 | 312 | [[package]] 313 | org = "wso2" 314 | name = "tweet_performance_tracker" 315 | version = "0.1.0" 316 | dependencies = [ 317 | {org = "ballerina", name = "io"}, 318 | {org = "ballerina", name = "lang.array"}, 319 | {org = "ballerinax", name = "twitter"} 320 | ] 321 | modules = [ 322 | {org = "wso2", packageName = "tweet_performance_tracker", moduleName = "tweet_performance_tracker"} 323 | ] 324 | 325 | -------------------------------------------------------------------------------- /ballerina/README.md: -------------------------------------------------------------------------------- 1 | ## Overview 2 | 3 | [Twitter(X)](https://about.twitter.com/) is a widely-used social networking service provided by X Corp., enabling users to post and interact with messages known as "tweets". 4 | 5 | The `ballerinax/twitter` package offers APIs to connect and interact with [Twitter(X) API](https://developer.twitter.com/en/docs/twitter-api) endpoints, specifically based on [Twitter(X) API v2](https://developer.x.com/en/docs/twitter-api/migrate/whats-new). 6 | 7 | ## Setup guide 8 | 9 | To use the Twitter connector, you must have access to the Twitter API through a [Twitter developer account](https://developer.twitter.com/en) and a project under it. If you do not have a Twitter Developer account, you can sign up for one [here](https://developer.twitter.com/en/apply-for-access). 10 | 11 | ### Step 1: Create a Twitter Developer Project 12 | 13 | 1. Open the [Twitter Developer Portal](https://developer.twitter.com/en/portal/dashboard). 14 | 15 | 2. Click on the "Projects & Apps" tab and select an existing project or create a new one for which you want API Keys and Authentication Tokens. 16 | 17 | Twitter Developer Portal 18 | 19 | ### Step 2: Set up user authentication settings 20 | 21 | 1. Scroll down and Click on the **Set up** button to set up user authentication. 22 | 23 | Set up 24 | 25 | 2. Complete the user authentication setup. 26 | 27 | ### Step 3. Obtain Client Id and Client Secret. 28 | 29 | 1. After completing the setup, you will be provided with your client Id and client secret. Make sure to save the provided client Id and client secret. 30 | 31 | Get Keys 32 | 33 | ### Step 4: Setup OAuth 2.0 Flow 34 | 35 | Before proceeding with the Quickstart, ensure you have obtained the Access Token using the following steps: 36 | 37 | 1. Create an authorization URL using the following format: 38 | 39 | ``` 40 | https://twitter.com/i/oauth2/authorize?response_type=code&client_id=&redirect_uri=&scope=tweet.read%20tweet.write%20users.read%20follows.read&state=state&code_challenge=&code_challenge_method=plain 41 | ``` 42 | 43 | Replace ``, ``, and `` with your specific values. Make sure to include the necessary scopes depending on your use case. 44 | 45 | **Note:** The "code verifier" is a randomly generated string used to verify the authorization code, and the "code challenge" is derived from the code verifier. These methods enhance security during the authorization process. 46 | In OAuth 2.0 PKCE, there are two methods for creating a "code challenge": 47 | 48 | 1. **S256**: The code challenge is a base64 URL-encoded SHA256 hash of a randomly generated string called the "code verifier". 49 | 50 | 2. **plain**: The code challenge is the plain code verifier string itself. 51 | 52 | Example authorization URL: 53 | 54 | ``` 55 | https://twitter.com/i/oauth2/authorize?response_type=code&client_id=asdasASDas21Y0OGR4bnUxSzA4c0k6MTpjaQ&redirect_uri=http://example&scope=tweet.read%20tweet.write%20users.read%20follows.read&state=state&code_challenge=D601XXCSK57UineGq62gUnsoasdas1GfKUY8QWhOF9hiN_k&code_challenge_method=plain 56 | ``` 57 | 58 | **Note:** By default, the access token you create through the OAuth 2.0 Flow, as used here, will only remain valid for two hours. There is an alternative way that does not invalidate the access token after 2 hours. To do this, refer to [Obtain access token under offline.access](https://developer.x.com/en/docs/authentication/oauth-2-0/user-access-token). 59 | 60 | 2. Copy and paste the generated URL into your browser. This will redirect you to the Twitter authorization page. 61 | 62 | Authorize Page 63 | 64 | 3. Once you authorize, you will be redirected to your specified redirect URI with an authorization code in the URL. 65 | 66 | Example: 67 | 68 | ``` 69 | http://www.example.com/?state=state&code=QjAtYldxeTZITnd5N0FVN1B3MlliU29rb1hrdmFPUWNXSG5LX1hCRExaeFE3OjE3MTkzODMzNjkxNjQ6MTowOmFjOjE 70 | ``` 71 | 72 | **Note:** Store the authorization code and use it promptly as it expires quickly. 73 | 74 | 4. Use the obtained authorization code to run the following curl command, replacing ``, ``, ``, and `` with your specific values: 75 | 76 | - Linux/MacOS: 77 | 78 | ```bash 79 | curl --location "https://api.twitter.com/2/oauth2/token" \ 80 | --header "Content-Type: application/x-www-form-urlencoded" \ 81 | --data-urlencode "code=" \ 82 | --data-urlencode "grant_type=authorization_code" \ 83 | --data-urlencode "client_id=" \ 84 | --data-urlencode "redirect_uri=" \ 85 | --data-urlencode "code_verifier=" 86 | ``` 87 | 88 | - Windows: 89 | 90 | ```bash 91 | curl --location "https://api.twitter.com/2/oauth2/token" ^ 92 | --header "Content-Type: application/x-www-form-urlencoded" ^ 93 | --data-urlencode "code=" ^ 94 | --data-urlencode "grant_type=authorization_code" ^ 95 | --data-urlencode "client_id=" ^ 96 | --data-urlencode "redirect_uri=" ^ 97 | --data-urlencode "code_verifier=" 98 | ``` 99 | 100 | This command will return the access token necessary for API calls. 101 | 102 | ```json 103 | { 104 | "token_type":"bearer", 105 | "expires_in":7200, 106 | "access_token":"VWdEaEQ2eEdGdmVSbUJQV1U5LUdWREZuYndVT1JaNDddsdsfdsfdsxcvIZGMzblNjRGtvb3dGOjE3MTkzNzYwOTQ1MDQ6MTowOmF0Oj", 107 | "scope":"tweet.write users.read follows.read tweet.read" 108 | } 109 | ``` 110 | 111 | 5. Store the access token securely for use in your application. 112 | 113 | **Note**: We recommend using the OAuth 2.0 Authorization Code with PKCE method as used here, but there is another way using OAuth 2.0 App Only [OAuth 2.0 App Only](https://developer.twitter.com/en/docs/authentication/oauth-2-0/application-only). Refer to this document to check which operations in Twitter API v2 are done using which method: [API reference](https://developer.twitter.com/en/docs/authentication/guides/v2-authentication-mapping). 114 | 115 | 116 | ## Quickstart 117 | 118 | To use the `Twitter` connector in your Ballerina application, update the `.bal` file as follows: 119 | 120 | ### Step 1: Import the module 121 | 122 | Import the `twitter` module. 123 | 124 | ```ballerina 125 | import ballerinax/twitter; 126 | ``` 127 | 128 | ### Step 2: Instantiate a new connector 129 | 130 | 1. Create a `Config.toml` file and, configure the obtained credentials in the above steps as follows: 131 | 132 | ```bash 133 | token = "" 134 | ``` 135 | 136 | 2. Create a `twitter:ConnectionConfig` with the obtained access token and initialize the connector with it. 137 | 138 | ```ballerina 139 | configurable string token = ?; 140 | 141 | final twitter:Client twitter = check new({ 142 | auth: { 143 | token 144 | } 145 | }); 146 | ``` 147 | 148 | ### Step 3: Invoke the connector operation 149 | 150 | Now, utilize the available connector operations. 151 | 152 | #### Post a tweet 153 | 154 | ```ballerina 155 | public function main() returns error? { 156 | twitter:TweetCreateResponse postTweet = check twitter->/tweets.post( 157 | payload = { 158 | text: "This is a sample tweet" 159 | } 160 | ); 161 | } 162 | ``` 163 | 164 | ### Step 4: Run the Ballerina application 165 | 166 | ```bash 167 | bal run 168 | ``` 169 | 170 | 171 | ## Examples 172 | 173 | The `Twitter` connector provides practical examples illustrating usage in various scenarios. Explore these [examples](https://github.com/ballerina-platform/module-ballerinax-twitter/tree/main/examples/), covering the following use cases: 174 | 175 | 1. [Direct message company mentions](https://github.com/ballerina-platform/module-ballerinax-twitter/tree/main/examples/DM-mentions) - Integrate Twitter to send direct messages to users who mention the company in tweets. 176 | 177 | 2. [Tweet performance tracker](https://github.com/ballerina-platform/module-ballerinax-twitter/tree/main/examples/tweet-performance-tracker) - Analyze the performance of tweets posted by a user over the past month. 178 | -------------------------------------------------------------------------------- /ballerina/utils.bal: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED FILE. DO NOT MODIFY. 2 | // This file is auto-generated by the Ballerina OpenAPI tool. 3 | 4 | // Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). 5 | // 6 | // WSO2 LLC. licenses this file to you under the Apache License, 7 | // Version 2.0 (the "License"); you may not use this file except 8 | // in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import ballerina/http; 21 | import ballerina/url; 22 | 23 | type SimpleBasicType string|boolean|int|float|decimal; 24 | 25 | # Represents encoding mechanism details. 26 | type Encoding record { 27 | # Defines how multiple values are delimited 28 | string style = FORM; 29 | # Specifies whether arrays and objects should generate as separate fields 30 | boolean explode = true; 31 | # Specifies the custom content type 32 | string contentType?; 33 | # Specifies the custom headers 34 | map headers?; 35 | }; 36 | 37 | enum EncodingStyle { 38 | DEEPOBJECT, FORM, SPACEDELIMITED, PIPEDELIMITED 39 | } 40 | 41 | final Encoding & readonly defaultEncoding = {}; 42 | 43 | # Serialize the record according to the deepObject style. 44 | # 45 | # + parent - Parent record name 46 | # + anyRecord - Record to be serialized 47 | # + return - Serialized record as a string 48 | isolated function getDeepObjectStyleRequest(string parent, record {} anyRecord) returns string { 49 | string[] recordArray = []; 50 | foreach [string, anydata] [key, value] in anyRecord.entries() { 51 | if value is SimpleBasicType { 52 | recordArray.push(parent + "[" + key + "]" + "=" + getEncodedUri(value.toString())); 53 | } else if value is SimpleBasicType[] { 54 | recordArray.push(getSerializedArray(parent + "[" + key + "]" + "[]", value, DEEPOBJECT, true)); 55 | } else if value is record {} { 56 | string nextParent = parent + "[" + key + "]"; 57 | recordArray.push(getDeepObjectStyleRequest(nextParent, value)); 58 | } else if value is record {}[] { 59 | string nextParent = parent + "[" + key + "]"; 60 | recordArray.push(getSerializedRecordArray(nextParent, value, DEEPOBJECT)); 61 | } 62 | recordArray.push("&"); 63 | } 64 | _ = recordArray.pop(); 65 | return string:'join("", ...recordArray); 66 | } 67 | 68 | # Serialize the record according to the form style. 69 | # 70 | # + parent - Parent record name 71 | # + anyRecord - Record to be serialized 72 | # + explode - Specifies whether arrays and objects should generate separate parameters 73 | # + return - Serialized record as a string 74 | isolated function getFormStyleRequest(string parent, record {} anyRecord, boolean explode = true) returns string { 75 | string[] recordArray = []; 76 | if explode { 77 | foreach [string, anydata] [key, value] in anyRecord.entries() { 78 | if value is SimpleBasicType { 79 | recordArray.push(key, "=", getEncodedUri(value.toString())); 80 | } else if value is SimpleBasicType[] { 81 | recordArray.push(getSerializedArray(key, value, explode = explode)); 82 | } else if value is record {} { 83 | recordArray.push(getFormStyleRequest(parent, value, explode)); 84 | } 85 | recordArray.push("&"); 86 | } 87 | _ = recordArray.pop(); 88 | } else { 89 | foreach [string, anydata] [key, value] in anyRecord.entries() { 90 | if value is SimpleBasicType { 91 | recordArray.push(key, ",", getEncodedUri(value.toString())); 92 | } else if value is SimpleBasicType[] { 93 | recordArray.push(getSerializedArray(key, value, explode = false)); 94 | } else if value is record {} { 95 | recordArray.push(getFormStyleRequest(parent, value, explode)); 96 | } 97 | recordArray.push(","); 98 | } 99 | _ = recordArray.pop(); 100 | } 101 | return string:'join("", ...recordArray); 102 | } 103 | 104 | # Serialize arrays. 105 | # 106 | # + arrayName - Name of the field with arrays 107 | # + anyArray - Array to be serialized 108 | # + style - Defines how multiple values are delimited 109 | # + explode - Specifies whether arrays and objects should generate separate parameters 110 | # + return - Serialized array as a string 111 | isolated function getSerializedArray(string arrayName, anydata[] anyArray, string style = "form", boolean explode = true) returns string { 112 | string key = arrayName; 113 | string[] arrayValues = []; 114 | if anyArray.length() > 0 { 115 | if style == FORM && !explode { 116 | arrayValues.push(key, "="); 117 | foreach anydata i in anyArray { 118 | arrayValues.push(getEncodedUri(i.toString()), ","); 119 | } 120 | } else if style == SPACEDELIMITED && !explode { 121 | arrayValues.push(key, "="); 122 | foreach anydata i in anyArray { 123 | arrayValues.push(getEncodedUri(i.toString()), "%20"); 124 | } 125 | } else if style == PIPEDELIMITED && !explode { 126 | arrayValues.push(key, "="); 127 | foreach anydata i in anyArray { 128 | arrayValues.push(getEncodedUri(i.toString()), "|"); 129 | } 130 | } else if style == DEEPOBJECT { 131 | foreach anydata i in anyArray { 132 | arrayValues.push(key, "[]", "=", getEncodedUri(i.toString()), "&"); 133 | } 134 | } else { 135 | foreach anydata i in anyArray { 136 | arrayValues.push(key, "=", getEncodedUri(i.toString()), "&"); 137 | } 138 | } 139 | _ = arrayValues.pop(); 140 | } 141 | return string:'join("", ...arrayValues); 142 | } 143 | 144 | # Serialize the array of records according to the form style. 145 | # 146 | # + parent - Parent record name 147 | # + value - Array of records to be serialized 148 | # + style - Defines how multiple values are delimited 149 | # + explode - Specifies whether arrays and objects should generate separate parameters 150 | # + return - Serialized record as a string 151 | isolated function getSerializedRecordArray(string parent, record {}[] value, string style = FORM, boolean explode = true) returns string { 152 | string[] serializedArray = []; 153 | if style == DEEPOBJECT { 154 | int arayIndex = 0; 155 | foreach var recordItem in value { 156 | serializedArray.push(getDeepObjectStyleRequest(parent + "[" + arayIndex.toString() + "]", recordItem), "&"); 157 | arayIndex = arayIndex + 1; 158 | } 159 | } else { 160 | if !explode { 161 | serializedArray.push(parent, "="); 162 | } 163 | foreach var recordItem in value { 164 | serializedArray.push(getFormStyleRequest(parent, recordItem, explode), ","); 165 | } 166 | } 167 | _ = serializedArray.pop(); 168 | return string:'join("", ...serializedArray); 169 | } 170 | 171 | # Get Encoded URI for a given value. 172 | # 173 | # + value - Value to be encoded 174 | # + return - Encoded string 175 | isolated function getEncodedUri(anydata value) returns string { 176 | string|error encoded = url:encode(value.toString(), "UTF8"); 177 | if encoded is string { 178 | return encoded; 179 | } else { 180 | return value.toString(); 181 | } 182 | } 183 | 184 | # Generate query path with query parameter. 185 | # 186 | # + queryParam - Query parameter map 187 | # + encodingMap - Details on serialization mechanism 188 | # + return - Returns generated Path or error at failure of client initialization 189 | isolated function getPathForQueryParam(map queryParam, map encodingMap = {}) returns string|error { 190 | map queriesMap = http:getQueryMap(queryParam); 191 | string[] param = []; 192 | if queriesMap.length() > 0 { 193 | param.push("?"); 194 | foreach var [key, value] in queriesMap.entries() { 195 | if value is () { 196 | _ = queriesMap.remove(key); 197 | continue; 198 | } 199 | Encoding encodingData = encodingMap.hasKey(key) ? encodingMap.get(key) : defaultEncoding; 200 | if value is SimpleBasicType { 201 | param.push(key, "=", getEncodedUri(value.toString())); 202 | } else if value is SimpleBasicType[] { 203 | param.push(getSerializedArray(key, value, encodingData.style, encodingData.explode)); 204 | } else if value is record {} { 205 | if encodingData.style == DEEPOBJECT { 206 | param.push(getDeepObjectStyleRequest(key, value)); 207 | } else { 208 | param.push(getFormStyleRequest(key, value, encodingData.explode)); 209 | } 210 | } else { 211 | param.push(key, "=", value.toString()); 212 | } 213 | param.push("&"); 214 | } 215 | _ = param.pop(); 216 | } 217 | string restOfPath = string:'join("", ...param); 218 | return restOfPath; 219 | } 220 | -------------------------------------------------------------------------------- /ballerina/Dependencies.toml: -------------------------------------------------------------------------------- 1 | # AUTO-GENERATED FILE. DO NOT MODIFY. 2 | 3 | # This file is auto-generated by Ballerina for managing dependency versions. 4 | # It should not be modified by hand. 5 | 6 | [ballerina] 7 | dependencies-toml-version = "2" 8 | distribution-version = "2201.12.2" 9 | 10 | [[package]] 11 | org = "ballerina" 12 | name = "auth" 13 | version = "2.14.0" 14 | dependencies = [ 15 | {org = "ballerina", name = "crypto"}, 16 | {org = "ballerina", name = "jballerina.java"}, 17 | {org = "ballerina", name = "lang.array"}, 18 | {org = "ballerina", name = "lang.string"}, 19 | {org = "ballerina", name = "log"} 20 | ] 21 | 22 | [[package]] 23 | org = "ballerina" 24 | name = "cache" 25 | version = "3.10.0" 26 | dependencies = [ 27 | {org = "ballerina", name = "constraint"}, 28 | {org = "ballerina", name = "jballerina.java"}, 29 | {org = "ballerina", name = "task"}, 30 | {org = "ballerina", name = "time"} 31 | ] 32 | 33 | [[package]] 34 | org = "ballerina" 35 | name = "constraint" 36 | version = "1.7.0" 37 | dependencies = [ 38 | {org = "ballerina", name = "jballerina.java"} 39 | ] 40 | modules = [ 41 | {org = "ballerina", packageName = "constraint", moduleName = "constraint"} 42 | ] 43 | 44 | [[package]] 45 | org = "ballerina" 46 | name = "crypto" 47 | version = "2.9.2" 48 | dependencies = [ 49 | {org = "ballerina", name = "jballerina.java"}, 50 | {org = "ballerina", name = "time"} 51 | ] 52 | 53 | [[package]] 54 | org = "ballerina" 55 | name = "data.jsondata" 56 | version = "1.1.3" 57 | dependencies = [ 58 | {org = "ballerina", name = "jballerina.java"}, 59 | {org = "ballerina", name = "lang.object"} 60 | ] 61 | modules = [ 62 | {org = "ballerina", packageName = "data.jsondata", moduleName = "data.jsondata"} 63 | ] 64 | 65 | [[package]] 66 | org = "ballerina" 67 | name = "file" 68 | version = "1.12.0" 69 | dependencies = [ 70 | {org = "ballerina", name = "io"}, 71 | {org = "ballerina", name = "jballerina.java"}, 72 | {org = "ballerina", name = "os"}, 73 | {org = "ballerina", name = "time"} 74 | ] 75 | 76 | [[package]] 77 | org = "ballerina" 78 | name = "http" 79 | version = "2.14.8" 80 | dependencies = [ 81 | {org = "ballerina", name = "auth"}, 82 | {org = "ballerina", name = "cache"}, 83 | {org = "ballerina", name = "constraint"}, 84 | {org = "ballerina", name = "crypto"}, 85 | {org = "ballerina", name = "data.jsondata"}, 86 | {org = "ballerina", name = "file"}, 87 | {org = "ballerina", name = "io"}, 88 | {org = "ballerina", name = "jballerina.java"}, 89 | {org = "ballerina", name = "jwt"}, 90 | {org = "ballerina", name = "lang.array"}, 91 | {org = "ballerina", name = "lang.decimal"}, 92 | {org = "ballerina", name = "lang.int"}, 93 | {org = "ballerina", name = "lang.regexp"}, 94 | {org = "ballerina", name = "lang.runtime"}, 95 | {org = "ballerina", name = "lang.string"}, 96 | {org = "ballerina", name = "lang.value"}, 97 | {org = "ballerina", name = "log"}, 98 | {org = "ballerina", name = "mime"}, 99 | {org = "ballerina", name = "oauth2"}, 100 | {org = "ballerina", name = "observe"}, 101 | {org = "ballerina", name = "time"}, 102 | {org = "ballerina", name = "url"} 103 | ] 104 | modules = [ 105 | {org = "ballerina", packageName = "http", moduleName = "http"}, 106 | {org = "ballerina", packageName = "http", moduleName = "http.httpscerr"} 107 | ] 108 | 109 | [[package]] 110 | org = "ballerina" 111 | name = "io" 112 | version = "1.8.0" 113 | dependencies = [ 114 | {org = "ballerina", name = "jballerina.java"}, 115 | {org = "ballerina", name = "lang.value"} 116 | ] 117 | 118 | [[package]] 119 | org = "ballerina" 120 | name = "jballerina.java" 121 | version = "0.0.0" 122 | 123 | [[package]] 124 | org = "ballerina" 125 | name = "jwt" 126 | version = "2.15.1" 127 | dependencies = [ 128 | {org = "ballerina", name = "cache"}, 129 | {org = "ballerina", name = "crypto"}, 130 | {org = "ballerina", name = "io"}, 131 | {org = "ballerina", name = "jballerina.java"}, 132 | {org = "ballerina", name = "lang.int"}, 133 | {org = "ballerina", name = "lang.string"}, 134 | {org = "ballerina", name = "log"}, 135 | {org = "ballerina", name = "time"} 136 | ] 137 | 138 | [[package]] 139 | org = "ballerina" 140 | name = "lang.__internal" 141 | version = "0.0.0" 142 | dependencies = [ 143 | {org = "ballerina", name = "jballerina.java"}, 144 | {org = "ballerina", name = "lang.object"} 145 | ] 146 | 147 | [[package]] 148 | org = "ballerina" 149 | name = "lang.array" 150 | version = "0.0.0" 151 | dependencies = [ 152 | {org = "ballerina", name = "jballerina.java"}, 153 | {org = "ballerina", name = "lang.__internal"} 154 | ] 155 | 156 | [[package]] 157 | org = "ballerina" 158 | name = "lang.decimal" 159 | version = "0.0.0" 160 | dependencies = [ 161 | {org = "ballerina", name = "jballerina.java"} 162 | ] 163 | 164 | [[package]] 165 | org = "ballerina" 166 | name = "lang.error" 167 | version = "0.0.0" 168 | scope = "testOnly" 169 | dependencies = [ 170 | {org = "ballerina", name = "jballerina.java"} 171 | ] 172 | 173 | [[package]] 174 | org = "ballerina" 175 | name = "lang.int" 176 | version = "0.0.0" 177 | dependencies = [ 178 | {org = "ballerina", name = "jballerina.java"}, 179 | {org = "ballerina", name = "lang.__internal"}, 180 | {org = "ballerina", name = "lang.object"} 181 | ] 182 | 183 | [[package]] 184 | org = "ballerina" 185 | name = "lang.object" 186 | version = "0.0.0" 187 | 188 | [[package]] 189 | org = "ballerina" 190 | name = "lang.regexp" 191 | version = "0.0.0" 192 | dependencies = [ 193 | {org = "ballerina", name = "jballerina.java"} 194 | ] 195 | 196 | [[package]] 197 | org = "ballerina" 198 | name = "lang.runtime" 199 | version = "0.0.0" 200 | dependencies = [ 201 | {org = "ballerina", name = "jballerina.java"} 202 | ] 203 | 204 | [[package]] 205 | org = "ballerina" 206 | name = "lang.string" 207 | version = "0.0.0" 208 | dependencies = [ 209 | {org = "ballerina", name = "jballerina.java"}, 210 | {org = "ballerina", name = "lang.regexp"} 211 | ] 212 | 213 | [[package]] 214 | org = "ballerina" 215 | name = "lang.value" 216 | version = "0.0.0" 217 | dependencies = [ 218 | {org = "ballerina", name = "jballerina.java"} 219 | ] 220 | 221 | [[package]] 222 | org = "ballerina" 223 | name = "log" 224 | version = "2.12.0" 225 | dependencies = [ 226 | {org = "ballerina", name = "io"}, 227 | {org = "ballerina", name = "jballerina.java"}, 228 | {org = "ballerina", name = "lang.value"}, 229 | {org = "ballerina", name = "observe"} 230 | ] 231 | modules = [ 232 | {org = "ballerina", packageName = "log", moduleName = "log"} 233 | ] 234 | 235 | [[package]] 236 | org = "ballerina" 237 | name = "mime" 238 | version = "2.12.1" 239 | dependencies = [ 240 | {org = "ballerina", name = "io"}, 241 | {org = "ballerina", name = "jballerina.java"}, 242 | {org = "ballerina", name = "lang.int"}, 243 | {org = "ballerina", name = "log"} 244 | ] 245 | 246 | [[package]] 247 | org = "ballerina" 248 | name = "oauth2" 249 | version = "2.14.1" 250 | dependencies = [ 251 | {org = "ballerina", name = "cache"}, 252 | {org = "ballerina", name = "crypto"}, 253 | {org = "ballerina", name = "jballerina.java"}, 254 | {org = "ballerina", name = "log"}, 255 | {org = "ballerina", name = "time"}, 256 | {org = "ballerina", name = "url"} 257 | ] 258 | 259 | [[package]] 260 | org = "ballerina" 261 | name = "observe" 262 | version = "1.5.1" 263 | dependencies = [ 264 | {org = "ballerina", name = "jballerina.java"} 265 | ] 266 | 267 | [[package]] 268 | org = "ballerina" 269 | name = "os" 270 | version = "1.10.1" 271 | dependencies = [ 272 | {org = "ballerina", name = "io"}, 273 | {org = "ballerina", name = "jballerina.java"} 274 | ] 275 | modules = [ 276 | {org = "ballerina", packageName = "os", moduleName = "os"} 277 | ] 278 | 279 | [[package]] 280 | org = "ballerina" 281 | name = "task" 282 | version = "2.7.0" 283 | dependencies = [ 284 | {org = "ballerina", name = "jballerina.java"}, 285 | {org = "ballerina", name = "time"} 286 | ] 287 | 288 | [[package]] 289 | org = "ballerina" 290 | name = "test" 291 | version = "0.0.0" 292 | scope = "testOnly" 293 | dependencies = [ 294 | {org = "ballerina", name = "jballerina.java"}, 295 | {org = "ballerina", name = "lang.array"}, 296 | {org = "ballerina", name = "lang.error"} 297 | ] 298 | modules = [ 299 | {org = "ballerina", packageName = "test", moduleName = "test"} 300 | ] 301 | 302 | [[package]] 303 | org = "ballerina" 304 | name = "time" 305 | version = "2.7.0" 306 | dependencies = [ 307 | {org = "ballerina", name = "jballerina.java"} 308 | ] 309 | modules = [ 310 | {org = "ballerina", packageName = "time", moduleName = "time"} 311 | ] 312 | 313 | [[package]] 314 | org = "ballerina" 315 | name = "url" 316 | version = "2.6.1" 317 | dependencies = [ 318 | {org = "ballerina", name = "jballerina.java"} 319 | ] 320 | modules = [ 321 | {org = "ballerina", packageName = "url", moduleName = "url"} 322 | ] 323 | 324 | [[package]] 325 | org = "ballerinai" 326 | name = "observe" 327 | version = "0.0.0" 328 | dependencies = [ 329 | {org = "ballerina", name = "jballerina.java"}, 330 | {org = "ballerina", name = "observe"} 331 | ] 332 | modules = [ 333 | {org = "ballerinai", packageName = "observe", moduleName = "observe"} 334 | ] 335 | 336 | [[package]] 337 | org = "ballerinax" 338 | name = "twitter" 339 | version = "5.0.0" 340 | dependencies = [ 341 | {org = "ballerina", name = "constraint"}, 342 | {org = "ballerina", name = "data.jsondata"}, 343 | {org = "ballerina", name = "http"}, 344 | {org = "ballerina", name = "log"}, 345 | {org = "ballerina", name = "os"}, 346 | {org = "ballerina", name = "test"}, 347 | {org = "ballerina", name = "time"}, 348 | {org = "ballerina", name = "url"}, 349 | {org = "ballerinai", name = "observe"} 350 | ] 351 | modules = [ 352 | {org = "ballerinax", packageName = "twitter", moduleName = "twitter"} 353 | ] 354 | 355 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit 88 | 89 | # Use the maximum available, or set MAX_FD != -1 to use that value. 90 | MAX_FD=maximum 91 | 92 | warn () { 93 | echo "$*" 94 | } >&2 95 | 96 | die () { 97 | echo 98 | echo "$*" 99 | echo 100 | exit 1 101 | } >&2 102 | 103 | # OS specific support (must be 'true' or 'false'). 104 | cygwin=false 105 | msys=false 106 | darwin=false 107 | nonstop=false 108 | case "$( uname )" in #( 109 | CYGWIN* ) cygwin=true ;; #( 110 | Darwin* ) darwin=true ;; #( 111 | MSYS* | MINGW* ) msys=true ;; #( 112 | NONSTOP* ) nonstop=true ;; 113 | esac 114 | 115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 116 | 117 | 118 | # Determine the Java command to use to start the JVM. 119 | if [ -n "$JAVA_HOME" ] ; then 120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 121 | # IBM's JDK on AIX uses strange locations for the executables 122 | JAVACMD=$JAVA_HOME/jre/sh/java 123 | else 124 | JAVACMD=$JAVA_HOME/bin/java 125 | fi 126 | if [ ! -x "$JAVACMD" ] ; then 127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 128 | 129 | Please set the JAVA_HOME variable in your environment to match the 130 | location of your Java installation." 131 | fi 132 | else 133 | JAVACMD=java 134 | if ! command -v java >/dev/null 2>&1 135 | then 136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | fi 142 | 143 | # Increase the maximum file descriptors if we can. 144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 145 | case $MAX_FD in #( 146 | max*) 147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 148 | # shellcheck disable=SC2039,SC3045 149 | MAX_FD=$( ulimit -H -n ) || 150 | warn "Could not query maximum file descriptor limit" 151 | esac 152 | case $MAX_FD in #( 153 | '' | soft) :;; #( 154 | *) 155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 156 | # shellcheck disable=SC2039,SC3045 157 | ulimit -n "$MAX_FD" || 158 | warn "Could not set maximum file descriptor limit to $MAX_FD" 159 | esac 160 | fi 161 | 162 | # Collect all arguments for the java command, stacking in reverse order: 163 | # * args from the command line 164 | # * the main class name 165 | # * -classpath 166 | # * -D...appname settings 167 | # * --module-path (only if needed) 168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 169 | 170 | # For Cygwin or MSYS, switch paths to Windows format before running java 171 | if "$cygwin" || "$msys" ; then 172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 174 | 175 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 176 | 177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 178 | for arg do 179 | if 180 | case $arg in #( 181 | -*) false ;; # don't mess with options #( 182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 183 | [ -e "$t" ] ;; #( 184 | *) false ;; 185 | esac 186 | then 187 | arg=$( cygpath --path --ignore --mixed "$arg" ) 188 | fi 189 | # Roll the args list around exactly as many times as the number of 190 | # args, so each arg winds up back in the position where it started, but 191 | # possibly modified. 192 | # 193 | # NB: a `for` loop captures its iteration list before it begins, so 194 | # changing the positional parameters here affects neither the number of 195 | # iterations, nor the values presented in `arg`. 196 | shift # remove old arg 197 | set -- "$@" "$arg" # push replacement arg 198 | done 199 | fi 200 | 201 | 202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 204 | 205 | # Collect all arguments for the java command: 206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 207 | # and any embedded shellness will be escaped. 208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 209 | # treated as '${Hostname}' itself on the command line. 210 | 211 | set -- \ 212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 213 | -classpath "$CLASSPATH" \ 214 | org.gradle.wrapper.GradleWrapperMain \ 215 | "$@" 216 | 217 | # Stop when "xargs" is not available. 218 | if ! command -v xargs >/dev/null 2>&1 219 | then 220 | die "xargs is not available" 221 | fi 222 | 223 | # Use "xargs" to parse quoted args. 224 | # 225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 226 | # 227 | # In Bash we could simply go: 228 | # 229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 230 | # set -- "${ARGS[@]}" "$@" 231 | # 232 | # but POSIX shell has neither arrays nor command substitution, so instead we 233 | # post-process each arg (as a line of input to sed) to backslash-escape any 234 | # character that might be a shell metacharacter, then use eval to reverse 235 | # that process (while maintaining the separation between arguments), and wrap 236 | # the whole thing up as a single "set" statement. 237 | # 238 | # This will of course break if any of these variables contains a newline or 239 | # an unmatched quote. 240 | # 241 | 242 | eval "set -- $( 243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 244 | xargs -n1 | 245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 246 | tr '\n' ' ' 247 | )" '"$@"' 248 | 249 | exec "$JAVACMD" "$@" 250 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /ballerina/tests/mock_service.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/http; 18 | import ballerina/log; 19 | 20 | listener http:Listener httpListener = new (9090); 21 | 22 | http:Service mockService = service object { 23 | 24 | # Remove a bookmarked Post 25 | # 26 | # + id - The ID of the authenticated source User whose bookmark is to be removed. 27 | # + tweet_id - The ID of the Post that the source User is removing from bookmarks. 28 | # + return - returns can be any of following types 29 | # http:Ok (The request has succeeded.) 30 | # http:Response (The request has failed.) 31 | resource function delete users/[UserIdMatchesAuthenticatedUser id]/bookmarks/[TweetId tweet_id]() returns BookmarkMutationResponse|http:Response { 32 | return { 33 | "data": {"bookmarked": false} 34 | }; 35 | } 36 | 37 | # Causes the User (in the path) to unlike the specified Post 38 | # 39 | # + id - The ID of the authenticated source User that is requesting to unlike the Post. 40 | # + tweet_id - The ID of the Post that the User is requesting to unlike. 41 | # + return - returns can be any of following types 42 | # http:Ok (The request has succeeded.) 43 | # http:Response (The request has failed.) 44 | resource function delete users/[UserIdMatchesAuthenticatedUser id]/likes/[TweetId tweet_id]() returns UsersLikesDeleteResponse|http:Response { 45 | return { 46 | "data": {"liked": false} 47 | }; 48 | } 49 | 50 | # Causes the User (in the path) to unretweet the specified Post 51 | # 52 | # + id - The ID of the authenticated source User that is requesting to repost the Post. 53 | # + source_tweet_id - The ID of the Post that the User is requesting to unretweet. 54 | # + return - returns can be any of following types 55 | # http:Ok (The request has succeeded.) 56 | # http:Response (The request has failed.) 57 | resource function delete users/[UserIdMatchesAuthenticatedUser id]/retweets/[TweetId source_tweet_id]() returns UsersRetweetsDeleteResponse|http:Response { 58 | return { 59 | "data": {"retweeted": false} 60 | }; 61 | } 62 | 63 | # Unfollow User 64 | # 65 | # + source_user_id - The ID of the authenticated source User that is requesting to unfollow the target User. 66 | # + target_user_id - The ID of the User that the source User is requesting to unfollow. 67 | # + return - returns can be any of following types 68 | # http:Ok (The request has succeeded.) 69 | # http:Response (The request has failed.) 70 | resource function delete users/[UserIdMatchesAuthenticatedUser source_user_id]/following/[UserId target_user_id]() returns UsersFollowingDeleteResponse|http:Response { 71 | return { 72 | "data": {"following": false} 73 | }; 74 | } 75 | 76 | # Unmute User by User ID 77 | # 78 | # + source_user_id - The ID of the authenticated source User that is requesting to unmute the target User. 79 | # + target_user_id - The ID of the User that the source User is requesting to unmute. 80 | # + return - returns can be any of following types 81 | # http:Ok (The request has succeeded.) 82 | # http:Response (The request has failed.) 83 | resource function delete users/[UserIdMatchesAuthenticatedUser source_user_id]/muting/[UserId target_user_id]() returns MuteUserMutationResponse|http:Response { 84 | return { 85 | "data": {"muting": false} 86 | }; 87 | } 88 | 89 | # Post lookup by Post ID 90 | # 91 | # + id - A single Post ID. 92 | # + tweet\.fields - A comma separated list of Tweet fields to display. 93 | # + expansions - A comma separated list of fields to expand. 94 | # + media\.fields - A comma separated list of Media fields to display. 95 | # + poll\.fields - A comma separated list of Poll fields to display. 96 | # + user\.fields - A comma separated list of User fields to display. 97 | # + place\.fields - A comma separated list of Place fields to display. 98 | # + return - returns can be any of following types 99 | # http:Ok (The request has succeeded.) 100 | # http:Response (The request has failed.) 101 | resource function get tweets/[TweetId id](("attachments"|"author_id"|"card_uri"|"context_annotations"|"conversation_id"|"created_at"|"edit_controls"|"edit_history_tweet_ids"|"entities"|"geo"|"id"|"in_reply_to_user_id"|"lang"|"non_public_metrics"|"note_tweet"|"organic_metrics"|"possibly_sensitive"|"promoted_metrics"|"public_metrics"|"referenced_tweets"|"reply_settings"|"scopes"|"source"|"text"|"username"|"withheld")[]? tweet\.fields, ("attachments.media_keys"|"attachments.media_source_tweet"|"attachments.poll_ids"|"author_id"|"edit_history_tweet_ids"|"entities.mentions.username"|"geo.place_id"|"in_reply_to_user_id"|"entities.note.mentions.username"|"referenced_tweets.id"|"referenced_tweets.id.author_id"|"author_screen_name")[]? expansions, ("alt_text"|"duration_ms"|"height"|"media_key"|"non_public_metrics"|"organic_metrics"|"preview_image_url"|"promoted_metrics"|"public_metrics"|"type"|"url"|"variants"|"width")[]? media\.fields, ("duration_minutes"|"end_datetime"|"id"|"options"|"voting_status")[]? poll\.fields, ("connection_status"|"created_at"|"description"|"entities"|"id"|"location"|"most_recent_tweet_id"|"name"|"pinned_tweet_id"|"profile_image_url"|"protected"|"public_metrics"|"receives_your_dm"|"subscription_type"|"url"|"username"|"verified"|"verified_type"|"withheld")[]? user\.fields, ("contained_within"|"country"|"country_code"|"full_name"|"geo"|"id"|"name"|"place_type")[]? place\.fields) returns Get2TweetsIdResponse|http:Response { 102 | return { 103 | "data": {"edit_history_tweet_ids": ["1806286701704462623"], "id": "1806286701704462623", "text": "aasbcascbasjbc"} 104 | }; 105 | } 106 | 107 | resource function get users/'by/username/[string username](("connection_status"|"created_at"|"description"|"entities"|"id"|"location"|"most_recent_tweet_id"|"name"|"pinned_tweet_id"|"profile_image_url"|"protected"|"public_metrics"|"receives_your_dm"|"subscription_type"|"url"|"username"|"verified"|"verified_type"|"withheld")[]? user\.fields, ("most_recent_tweet_id"|"pinned_tweet_id")[]? expansions, ("attachments"|"author_id"|"card_uri"|"context_annotations"|"conversation_id"|"created_at"|"edit_controls"|"edit_history_tweet_ids"|"entities"|"geo"|"id"|"in_reply_to_user_id"|"lang"|"non_public_metrics"|"note_tweet"|"organic_metrics"|"possibly_sensitive"|"promoted_metrics"|"public_metrics"|"referenced_tweets"|"reply_settings"|"scopes"|"source"|"text"|"username"|"withheld")[]? tweet\.fields) returns Get2UsersByUsernameUsernameResponse|http:Response { 108 | return { 109 | "data": {"id": "350224247", "name": "Kumar Sangakkara", "username": "KumarSanga2"} 110 | }; 111 | } 112 | 113 | # Creation of a Post 114 | # 115 | # + return - returns can be any of following types 116 | # http:Created (The request has succeeded.) 117 | # http:Response (The request has failed.) 118 | resource function post tweets(@http:Payload TweetCreateRequest payload) returns TweetCreateResponse|http:Response { 119 | return { 120 | "data": {"id": "1807808193139204482", "text": "Twitter Test at[1719850035,0.227505100]", "edit_history_tweet_ids": ["1807808193139204482"]} 121 | }; 122 | } 123 | 124 | # Add Post to Bookmarks 125 | # 126 | # + id - The ID of the authenticated source User for whom to add bookmarks. 127 | # + return - returns can be any of following types 128 | # http:Ok (The request has succeeded.) 129 | # http:Response (The request has failed.) 130 | resource function post users/[UserIdMatchesAuthenticatedUser id]/bookmarks(@http:Payload BookmarkAddRequest payload) returns BookmarkMutationResponse|http:Response { 131 | return { 132 | "data": {"bookmarked": true} 133 | }; 134 | } 135 | 136 | # Follow User 137 | # 138 | # + id - The ID of the authenticated source User that is requesting to follow the target User. 139 | # + return - returns can be any of following types 140 | # http:Ok (The request has succeeded.) 141 | # http:Response (The request has failed.) 142 | resource function post users/[UserIdMatchesAuthenticatedUser id]/following(@http:Payload UsersFollowingCreateRequest payload) returns UsersFollowingCreateResponse|http:Response { 143 | return { 144 | "data": {"following": true, "pending_follow": false} 145 | }; 146 | } 147 | 148 | # Causes the User (in the path) to like the specified Post 149 | # 150 | # + id - The ID of the authenticated source User that is requesting to like the Post. 151 | # + return - returns can be any of following types 152 | # http:Ok (The request has succeeded.) 153 | # http:Response (The request has failed.) 154 | resource function post users/[UserIdMatchesAuthenticatedUser id]/likes(@http:Payload UsersLikesCreateRequest payload) returns UsersLikesCreateResponse|http:Response { 155 | return { 156 | "data": {"liked": true} 157 | }; 158 | } 159 | 160 | # Mute User by User ID. 161 | # 162 | # + id - The ID of the authenticated source User that is requesting to mute the target User. 163 | # + return - returns can be any of following types 164 | # http:Ok (The request has succeeded.) 165 | # http:Response (The request has failed.) 166 | resource function post users/[UserIdMatchesAuthenticatedUser id]/muting(@http:Payload MuteUserRequest payload) returns MuteUserMutationResponse|http:Response { 167 | return { 168 | "data": {"muting": true} 169 | }; 170 | } 171 | 172 | # Causes the User (in the path) to repost the specified Post. 173 | # 174 | # + id - The ID of the authenticated source User that is requesting to repost the Post. 175 | # + return - returns can be any of following types 176 | # http:Ok (The request has succeeded.) 177 | # http:Response (The request has failed.) 178 | resource function post users/[UserIdMatchesAuthenticatedUser id]/retweets(@http:Payload UsersRetweetsCreateRequest payload) returns UsersRetweetsCreateResponse|http:Response { 179 | return { 180 | "data": {"retweeted": true, "rest_id": "1807808194787590411"} 181 | }; 182 | } 183 | 184 | # User lookup by IDs 185 | # 186 | # + ids - A list of User IDs, comma-separated. You can specify up to 100 IDs. 187 | # + user\.fields - A comma separated list of User fields to display. 188 | # + expansions - A comma separated list of fields to expand. 189 | # + tweet\.fields - A comma separated list of Tweet fields to display. 190 | # + return - returns can be any of following types 191 | # http:Ok (The request has succeeded.) 192 | # http:Response (The request has failed.) 193 | resource function get users(UserId[] ids, ("connection_status"|"created_at"|"description"|"entities"|"id"|"location"|"most_recent_tweet_id"|"name"|"pinned_tweet_id"|"profile_image_url"|"protected"|"public_metrics"|"receives_your_dm"|"subscription_type"|"url"|"username"|"verified"|"verified_type"|"withheld")[]? user\.fields, ("most_recent_tweet_id"|"pinned_tweet_id")[]? expansions, ("attachments"|"author_id"|"card_uri"|"context_annotations"|"conversation_id"|"created_at"|"edit_controls"|"edit_history_tweet_ids"|"entities"|"geo"|"id"|"in_reply_to_user_id"|"lang"|"non_public_metrics"|"note_tweet"|"organic_metrics"|"possibly_sensitive"|"promoted_metrics"|"public_metrics"|"referenced_tweets"|"reply_settings"|"scopes"|"source"|"text"|"username"|"withheld")[]? tweet\.fields) returns Get2UsersResponse|http:Response { 194 | return { 195 | "data": [{"id": "350224247", "name": "Kumar Sangakkara", "username": "KumarSanga2"}] 196 | }; 197 | } 198 | }; 199 | 200 | function init() returns error? { 201 | if isLiveServer { 202 | log:printInfo("Skiping mock server initialization as the tests are running on live server"); 203 | return; 204 | } 205 | log:printInfo("Initiating mock server"); 206 | check httpListener.attach(mockService, "/"); 207 | check httpListener.'start(); 208 | } 209 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ballerina Twitter Connector 2 | 3 | [![Build](https://github.com/ballerina-platform/module-ballerinax-twitter/workflows/CI/badge.svg)](https://github.com/ballerina-platform/module-ballerinax-twitter/actions?query=workflow%3ACI) 4 | [![codecov](https://codecov.io/gh/ballerina-platform/module-ballerinax-twitter/branch/main/graph/badge.svg)](https://codecov.io/gh/ballerina-platform/module-ballerinax-twitter) 5 | [![GitHub Last Commit](https://img.shields.io/github/last-commit/ballerina-platform/module-ballerinax-twitter.svg)](https://github.com/ballerina-platform/module-ballerinax-twitter/commits/master) 6 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 7 | 8 | 9 | ## Overview 10 | 11 | [Twitter(X)](https://about.twitter.com/) is a widely-used social networking service provided by X Corp., enabling users to post and interact with messages known as "tweets". 12 | 13 | The `ballerinax/twitter` package offers APIs to connect and interact with [Twitter(X) API](https://developer.twitter.com/en/docs/twitter-api) endpoints, specifically based on [Twitter(X) API v2](https://developer.x.com/en/docs/twitter-api/migrate/whats-new). 14 | 15 | ## Setup guide 16 | 17 | To use the Twitter connector, you must have access to the Twitter API through a [Twitter developer account](https://developer.twitter.com/en) and a project under it. If you do not have a Twitter Developer account, you can sign up for one [here](https://developer.twitter.com/en/apply-for-access). 18 | 19 | ### Step 1: Create a Twitter Developer Project 20 | 21 | 1. Open the [Twitter Developer Portal](https://developer.twitter.com/en/portal/dashboard). 22 | 23 | 2. Click on the "Projects & Apps" tab and select an existing project or create a new one for which you want API Keys and Authentication Tokens. 24 | 25 | Twitter Developer Portal 26 | 27 | ### Step 2: Set up user authentication settings 28 | 29 | 1. Scroll down and Click on the **Set up** button to set up user authentication. 30 | 31 | Set up 32 | 33 | 2. Complete the user authentication setup. 34 | 35 | ### Step 3. Obtain Client Id and Client Secret. 36 | 37 | 1. After completing the setup, you will be provided with your client Id and client secret. Make sure to save the provided client Id and client secret. 38 | 39 | Get Keys 40 | 41 | ### Step 4: Setup OAuth 2.0 Flow 42 | 43 | Before proceeding with the Quickstart, ensure you have obtained the Access Token using the following steps: 44 | 45 | 1. Create an authorization URL using the following format: 46 | 47 | ``` 48 | https://twitter.com/i/oauth2/authorize?response_type=code&client_id=&redirect_uri=&scope=tweet.read%20tweet.write%20users.read%20follows.read&state=state&code_challenge=&code_challenge_method=plain 49 | ``` 50 | 51 | Replace ``, ``, and `` with your specific values. Make sure to include the necessary scopes depending on your use case. 52 | 53 | **Note:** The "code verifier" is a randomly generated string used to verify the authorization code, and the "code challenge" is derived from the code verifier. These methods enhance security during the authorization process. 54 | In OAuth 2.0 PKCE, there are two methods for creating a "code challenge": 55 | 56 | 1. **S256**: The code challenge is a base64 URL-encoded SHA256 hash of a randomly generated string called the "code verifier". 57 | 58 | 2. **plain**: The code challenge is the plain code verifier string itself. 59 | 60 | Example authorization URL: 61 | 62 | ``` 63 | https://twitter.com/i/oauth2/authorize?response_type=code&client_id=asdasASDas21Y0OGR4bnUxSzA4c0k6MTpjaQ&redirect_uri=http://example&scope=tweet.read%20tweet.write%20users.read%20follows.read&state=state&code_challenge=D601XXCSK57UineGq62gUnsoasdas1GfKUY8QWhOF9hiN_k&code_challenge_method=plain 64 | ``` 65 | 66 | **Note:** By default, the access token you create through the OAuth 2.0 Flow, as used here, will only remain valid for two hours. There is an alternative way that does not invalidate the access token after 2 hours. To do this, refer to [Obtain access token under offline.access](https://developer.x.com/en/docs/authentication/oauth-2-0/user-access-token). 67 | 68 | 2. Copy and paste the generated URL into your browser. This will redirect you to the Twitter authorization page. 69 | 70 | Authorize Page 71 | 72 | 3. Once you authorize, you will be redirected to your specified redirect URI with an authorization code in the URL. 73 | 74 | Example: 75 | 76 | ``` 77 | http://www.example.com/?state=state&code=QjAtYldxeTZITnd5N0FVN1B3MlliU29rb1hrdmFPUWNXSG5LX1hCRExaeFE3OjE3MTkzODMzNjkxNjQ6MTowOmFjOjE 78 | ``` 79 | 80 | **Note:** Store the authorization code and use it promptly as it expires quickly. 81 | 82 | 4. Use the obtained authorization code to run the following curl command, replacing ``, ``, ``, and `` with your specific values: 83 | 84 | - Linux/MacOS: 85 | 86 | ```bash 87 | curl --location "https://api.twitter.com/2/oauth2/token" \ 88 | --header "Content-Type: application/x-www-form-urlencoded" \ 89 | --data-urlencode "code=" \ 90 | --data-urlencode "grant_type=authorization_code" \ 91 | --data-urlencode "client_id=" \ 92 | --data-urlencode "redirect_uri=" \ 93 | --data-urlencode "code_verifier=" 94 | ``` 95 | 96 | - Windows: 97 | 98 | ```bash 99 | curl --location "https://api.twitter.com/2/oauth2/token" ^ 100 | --header "Content-Type: application/x-www-form-urlencoded" ^ 101 | --data-urlencode "code=" ^ 102 | --data-urlencode "grant_type=authorization_code" ^ 103 | --data-urlencode "client_id=" ^ 104 | --data-urlencode "redirect_uri=" ^ 105 | --data-urlencode "code_verifier=" 106 | ``` 107 | 108 | This command will return the access token necessary for API calls. 109 | 110 | ```json 111 | { 112 | "token_type":"bearer", 113 | "expires_in":7200, 114 | "access_token":"VWdEaEQ2eEdGdmVSbUJQV1U5LUdWREZuYndVT1JaNDddsdsfdsfdsxcvIZGMzblNjRGtvb3dGOjE3MTkzNzYwOTQ1MDQ6MTowOmF0Oj", 115 | "scope":"tweet.write users.read follows.read tweet.read" 116 | } 117 | ``` 118 | 119 | 5. Store the access token securely for use in your application. 120 | 121 | **Note**: We recommend using the OAuth 2.0 Authorization Code with PKCE method as used here, but there is another way using OAuth 2.0 App Only [OAuth 2.0 App Only](https://developer.twitter.com/en/docs/authentication/oauth-2-0/application-only). Refer to this document to check which operations in Twitter API v2 are done using which method: [API reference](https://developer.twitter.com/en/docs/authentication/guides/v2-authentication-mapping). 122 | 123 | 124 | ## Quickstart 125 | 126 | To use the `Twitter` connector in your Ballerina application, update the `.bal` file as follows: 127 | 128 | ### Step 1: Import the module 129 | 130 | Import the `twitter` module. 131 | 132 | ```ballerina 133 | import ballerinax/twitter; 134 | ``` 135 | 136 | ### Step 2: Instantiate a new connector 137 | 138 | 1. Create a `Config.toml` file and, configure the obtained credentials in the above steps as follows: 139 | 140 | ```bash 141 | token = "" 142 | ``` 143 | 144 | 2. Create a `twitter:ConnectionConfig` with the obtained access token and initialize the connector with it. 145 | 146 | ```ballerina 147 | configurable string token = ?; 148 | 149 | final twitter:Client twitter = check new({ 150 | auth: { 151 | token 152 | } 153 | }); 154 | ``` 155 | 156 | ### Step 3: Invoke the connector operation 157 | 158 | Now, utilize the available connector operations. 159 | 160 | #### Post a tweet 161 | 162 | ```ballerina 163 | public function main() returns error? { 164 | twitter:TweetCreateResponse postTweet = check twitter->/tweets.post( 165 | payload = { 166 | text: "This is a sample tweet" 167 | } 168 | ); 169 | } 170 | ``` 171 | 172 | ### Step 4: Run the Ballerina application 173 | 174 | ```bash 175 | bal run 176 | ``` 177 | 178 | 179 | ## Examples 180 | 181 | The `Twitter` connector provides practical examples illustrating usage in various scenarios. Explore these [examples](https://github.com/ballerina-platform/module-ballerinax-twitter/tree/main/examples/), covering the following use cases: 182 | 183 | 1. [Direct message company mentions](https://github.com/ballerina-platform/module-ballerinax-twitter/tree/main/examples/DM-mentions) - Integrate Twitter to send direct messages to users who mention the company in tweets. 184 | 185 | 2. [Tweet performance tracker](https://github.com/ballerina-platform/module-ballerinax-twitter/tree/main/examples/tweet-performance-tracker) - Analyze the performance of tweets posted by a user over the past month. 186 | 187 | 188 | ## Issues and projects 189 | 190 | The **Issues** and **Projects** tabs are disabled for this repository as this is part of the Ballerina library. To report bugs, request new features, start new discussions, view project boards, etc., visit the Ballerina library [parent repository](https://github.com/ballerina-platform/ballerina-library). 191 | 192 | This repository only contains the source code for the package. 193 | 194 | 195 | ## Building from the source 196 | 197 | ### Prerequisites 198 | 199 | 1. Download and install Java SE Development Kit (JDK) version 17. You can download it from either of the following sources: 200 | 201 | * [Oracle JDK](https://www.oracle.com/java/technologies/downloads/) 202 | * [OpenJDK](https://adoptium.net/) 203 | 204 | > **Note:** After installation, remember to set the `JAVA_HOME` environment variable to the directory where JDK was installed. 205 | 206 | 2. Download and install [Ballerina Swan Lake](https://ballerina.io/). 207 | 208 | 3. Download and install [Docker](https://www.docker.com/get-started). 209 | 210 | > **Note**: Ensure that the Docker daemon is running before executing any tests. 211 | 212 | 4. Export Github Personal access token with read package permissions as follows, 213 | 214 | ```bash 215 | export packageUser= 216 | export packagePAT= 217 | ``` 218 | 219 | 220 | ### Build options 221 | 222 | Execute the commands below to build from the source. 223 | 224 | 1. To build the package: 225 | 226 | ```bash 227 | ./gradlew clean build 228 | ``` 229 | 230 | 2. To run the tests: 231 | 232 | ```bash 233 | ./gradlew clean test 234 | ``` 235 | 236 | 3. To build the without the tests: 237 | 238 | ```bash 239 | ./gradlew clean build -x test 240 | ``` 241 | 242 | 4. To run tests against different environment: 243 | 244 | ```bash 245 | ./gradlew clean test -Pgroups= 246 | ``` 247 | 248 | 5. To debug package with a remote debugger: 249 | 250 | ```bash 251 | ./gradlew clean build -Pdebug= 252 | ``` 253 | 254 | 6. To debug with the Ballerina language: 255 | 256 | ```bash 257 | ./gradlew clean build -PbalJavaDebug= 258 | ``` 259 | 260 | 7. Publish the generated artifacts to the local Ballerina Central repository: 261 | 262 | ```bash 263 | ./gradlew clean build -PpublishToLocalCentral=true 264 | ``` 265 | 266 | 8. Publish the generated artifacts to the Ballerina Central repository: 267 | 268 | ```bash 269 | ./gradlew clean build -PpublishToCentral=true 270 | ``` 271 | ## Contributing to Ballerina 272 | As an open source project, Ballerina welcomes contributions from the community. 273 | 274 | For more information, see the [Contribution Guidelines](https://github.com/ballerina-platform/ballerina-lang/blob/master/CONTRIBUTING.md). 275 | 276 | ## Code of conduct 277 | All contributors are encouraged to read the [Ballerina Code of Conduct](https://ballerina.io/code-of-conduct). 278 | 279 | ## Useful links 280 | * Discuss about code changes of the Ballerina project via [ballerina-dev@googlegroups.com](mailto:ballerina-dev@googlegroups.com). 281 | * Chat live with us via our [Discord server](https://discord.gg/ballerinalang). 282 | * Post all technical questions on Stack Overflow with the [#ballerina](https://stackoverflow.com/questions/tagged/ballerina) tag. 283 | -------------------------------------------------------------------------------- /ballerina/client.bal: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED FILE. DO NOT MODIFY. 2 | // This file is auto-generated by the Ballerina OpenAPI tool. 3 | 4 | // Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). 5 | // 6 | // WSO2 LLC. licenses this file to you under the Apache License, 7 | // Version 2.0 (the "License"); you may not use this file except 8 | // in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import ballerina/data.jsondata; 21 | import ballerina/http; 22 | 23 | # Twitter API v2 available endpoints 24 | public isolated client class Client { 25 | final http:Client clientEp; 26 | # Gets invoked to initialize the `connector`. 27 | # 28 | # + config - The configurations to be used when initializing the `connector` 29 | # + serviceUrl - URL of the target service 30 | # + return - An error if connector initialization failed 31 | public isolated function init(ConnectionConfig config, string serviceUrl = "https://api.twitter.com/2") returns error? { 32 | http:ClientConfiguration httpClientConfig = {auth: config.auth, httpVersion: config.httpVersion, http1Settings: config.http1Settings, http2Settings: config.http2Settings, timeout: config.timeout, forwarded: config.forwarded, followRedirects: config.followRedirects, poolConfig: config.poolConfig, cache: config.cache, compression: config.compression, circuitBreaker: config.circuitBreaker, retryConfig: config.retryConfig, cookieConfig: config.cookieConfig, responseLimits: config.responseLimits, secureSocket: config.secureSocket, proxy: config.proxy, socketConfig: config.socketConfig, validation: config.validation, laxDataBinding: config.laxDataBinding}; 33 | self.clientEp = check new (serviceUrl, httpClientConfig); 34 | } 35 | 36 | # List Compliance Jobs 37 | # 38 | # + headers - Headers to be sent with the request 39 | # + queries - Queries to be sent with the request 40 | # + return - The request has succeeded 41 | resource isolated function get compliance/jobs(map headers = {}, *ListBatchComplianceJobsQueries queries) returns Get2ComplianceJobsResponse|error { 42 | string resourcePath = string `/compliance/jobs`; 43 | map queryParamEncoding = {"compliance_job.fields": {style: FORM, explode: false}}; 44 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 45 | return self.clientEp->get(resourcePath, headers); 46 | } 47 | 48 | # Create compliance job 49 | # 50 | # + headers - Headers to be sent with the request 51 | # + return - The request has succeeded 52 | resource isolated function post compliance/jobs(CreateComplianceJobRequest payload, map headers = {}) returns CreateComplianceJobResponse|error { 53 | string resourcePath = string `/compliance/jobs`; 54 | http:Request request = new; 55 | json jsonBody = jsondata:toJson(payload); 56 | request.setPayload(jsonBody, "application/json"); 57 | return self.clientEp->post(resourcePath, request, headers); 58 | } 59 | 60 | # Get Compliance Job 61 | # 62 | # + id - The ID of the Compliance Job to retrieve 63 | # + headers - Headers to be sent with the request 64 | # + queries - Queries to be sent with the request 65 | # + return - The request has succeeded 66 | resource isolated function get compliance/jobs/[JobId id](map headers = {}, *GetBatchComplianceJobQueries queries) returns Get2ComplianceJobsIdResponse|error { 67 | string resourcePath = string `/compliance/jobs/${getEncodedUri(id)}`; 68 | map queryParamEncoding = {"compliance_job.fields": {style: FORM, explode: false}}; 69 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 70 | return self.clientEp->get(resourcePath, headers); 71 | } 72 | 73 | # Create a new DM Conversation 74 | # 75 | # + headers - Headers to be sent with the request 76 | # + return - The request has succeeded 77 | resource isolated function post dm_conversations(CreateDmConversationRequest payload, map headers = {}) returns CreateDmEventResponse|error { 78 | string resourcePath = string `/dm_conversations`; 79 | http:Request request = new; 80 | json jsonBody = jsondata:toJson(payload); 81 | request.setPayload(jsonBody, "application/json"); 82 | return self.clientEp->post(resourcePath, request, headers); 83 | } 84 | 85 | # Get DM Events for a DM Conversation 86 | # 87 | # + participantId - The ID of the participant user for the One to One DM conversation 88 | # + headers - Headers to be sent with the request 89 | # + queries - Queries to be sent with the request 90 | # + return - The request has succeeded 91 | resource isolated function get dm_conversations/with/[UserId participantId]/dm_events(map headers = {}, *GetDmConversationsWithParticipantIdDmEventsQueries queries) returns Get2DmConversationsWithParticipantIdDmEventsResponse|error { 92 | string resourcePath = string `/dm_conversations/with/${getEncodedUri(participantId)}/dm_events`; 93 | map queryParamEncoding = {"event_types": {style: FORM, explode: false}, "dm_event.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "media.fields": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "tweet.fields": {style: FORM, explode: false}}; 94 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 95 | return self.clientEp->get(resourcePath, headers); 96 | } 97 | 98 | # Send a new message to a user 99 | # 100 | # + participantId - The ID of the recipient user that will receive the DM 101 | # + headers - Headers to be sent with the request 102 | # + return - The request has succeeded 103 | resource isolated function post dm_conversations/with/[UserId participantId]/messages(CreateMessageRequest payload, map headers = {}) returns CreateDmEventResponse|error { 104 | string resourcePath = string `/dm_conversations/with/${getEncodedUri(participantId)}/messages`; 105 | http:Request request = new; 106 | json jsonBody = jsondata:toJson(payload); 107 | request.setPayload(jsonBody, "application/json"); 108 | return self.clientEp->post(resourcePath, request, headers); 109 | } 110 | 111 | # Send a new message to a DM Conversation 112 | # 113 | # + dmConversationId - The DM Conversation ID 114 | # + headers - Headers to be sent with the request 115 | # + return - The request has succeeded 116 | resource isolated function post dm_conversations/[string dmConversationId]/messages(CreateMessageRequest payload, map headers = {}) returns CreateDmEventResponse|error { 117 | string resourcePath = string `/dm_conversations/${getEncodedUri(dmConversationId)}/messages`; 118 | http:Request request = new; 119 | json jsonBody = jsondata:toJson(payload); 120 | request.setPayload(jsonBody, "application/json"); 121 | return self.clientEp->post(resourcePath, request, headers); 122 | } 123 | 124 | # Get DM Events for a DM Conversation 125 | # 126 | # + id - The DM Conversation ID 127 | # + headers - Headers to be sent with the request 128 | # + queries - Queries to be sent with the request 129 | # + return - The request has succeeded 130 | resource isolated function get dm_conversations/[DmConversationId id]/dm_events(map headers = {}, *GetDmConversationsIdDmEventsQueries queries) returns Get2DmConversationsIdDmEventsResponse|error { 131 | string resourcePath = string `/dm_conversations/${getEncodedUri(id)}/dm_events`; 132 | map queryParamEncoding = {"event_types": {style: FORM, explode: false}, "dm_event.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "media.fields": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "tweet.fields": {style: FORM, explode: false}}; 133 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 134 | return self.clientEp->get(resourcePath, headers); 135 | } 136 | 137 | # Get recent DM Events 138 | # 139 | # + headers - Headers to be sent with the request 140 | # + queries - Queries to be sent with the request 141 | # + return - The request has succeeded 142 | resource isolated function get dm_events(map headers = {}, *GetDmEventsQueries queries) returns Get2DmEventsResponse|error { 143 | string resourcePath = string `/dm_events`; 144 | map queryParamEncoding = {"event_types": {style: FORM, explode: false}, "dm_event.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "media.fields": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "tweet.fields": {style: FORM, explode: false}}; 145 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 146 | return self.clientEp->get(resourcePath, headers); 147 | } 148 | 149 | # Get DM Events by id 150 | # 151 | # + eventId - dm event id 152 | # + headers - Headers to be sent with the request 153 | # + queries - Queries to be sent with the request 154 | # + return - The request has succeeded 155 | resource isolated function get dm_events/[DmEventId eventId](map headers = {}, *GetDmEventsByIdQueries queries) returns Get2DmEventsEventIdResponse|error { 156 | string resourcePath = string `/dm_events/${getEncodedUri(eventId)}`; 157 | map queryParamEncoding = {"dm_event.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "media.fields": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "tweet.fields": {style: FORM, explode: false}}; 158 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 159 | return self.clientEp->get(resourcePath, headers); 160 | } 161 | 162 | # Delete Dm 163 | # 164 | # + eventId - The ID of the direct-message event to delete 165 | # + headers - Headers to be sent with the request 166 | # + return - The request has succeeded 167 | resource isolated function delete dm_events/[DmEventId eventId](map headers = {}) returns DeleteDmResponse|error { 168 | string resourcePath = string `/dm_events/${getEncodedUri(eventId)}`; 169 | return self.clientEp->delete(resourcePath, headers = headers); 170 | } 171 | 172 | # Likes Compliance stream 173 | # 174 | # + headers - Headers to be sent with the request 175 | # + queries - Queries to be sent with the request 176 | # + return - The request has succeeded 177 | resource isolated function get likes/compliance/'stream(map headers = {}, *GetLikesComplianceStreamQueries queries) returns LikesComplianceStreamResponse|error { 178 | string resourcePath = string `/likes/compliance/stream`; 179 | resourcePath = resourcePath + check getPathForQueryParam(queries); 180 | return self.clientEp->get(resourcePath, headers); 181 | } 182 | 183 | # Likes Firehose stream 184 | # 185 | # + headers - Headers to be sent with the request 186 | # + queries - Queries to be sent with the request 187 | # + return - The request has succeeded 188 | resource isolated function get likes/firehose/'stream(map headers = {}, *LikesFirehoseStreamQueries queries) returns StreamingLikeResponse|error { 189 | string resourcePath = string `/likes/firehose/stream`; 190 | map queryParamEncoding = {"like.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "tweet.fields": {style: FORM, explode: false}}; 191 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 192 | return self.clientEp->get(resourcePath, headers); 193 | } 194 | 195 | # Likes Sample 10 stream 196 | # 197 | # + headers - Headers to be sent with the request 198 | # + queries - Queries to be sent with the request 199 | # + return - The request has succeeded 200 | resource isolated function get likes/sample10/'stream(map headers = {}, *LikesSample10StreamQueries queries) returns StreamingLikeResponse|error { 201 | string resourcePath = string `/likes/sample10/stream`; 202 | map queryParamEncoding = {"like.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "tweet.fields": {style: FORM, explode: false}}; 203 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 204 | return self.clientEp->get(resourcePath, headers); 205 | } 206 | 207 | # Create List 208 | # 209 | # + headers - Headers to be sent with the request 210 | # + return - The request has succeeded 211 | resource isolated function post lists(ListCreateRequest payload, map headers = {}) returns ListCreateResponse|error { 212 | string resourcePath = string `/lists`; 213 | http:Request request = new; 214 | json jsonBody = jsondata:toJson(payload); 215 | request.setPayload(jsonBody, "application/json"); 216 | return self.clientEp->post(resourcePath, request, headers); 217 | } 218 | 219 | # List lookup by List ID. 220 | # 221 | # + id - The ID of the List 222 | # + headers - Headers to be sent with the request 223 | # + queries - Queries to be sent with the request 224 | # + return - The request has succeeded 225 | resource isolated function get lists/[ListId id](map headers = {}, *ListIdGetQueries queries) returns Get2ListsIdResponse|error { 226 | string resourcePath = string `/lists/${getEncodedUri(id)}`; 227 | map queryParamEncoding = {"list.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}}; 228 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 229 | return self.clientEp->get(resourcePath, headers); 230 | } 231 | 232 | # Update List. 233 | # 234 | # + id - The ID of the List to modify 235 | # + headers - Headers to be sent with the request 236 | # + return - The request has succeeded 237 | resource isolated function put lists/[ListId id](ListUpdateRequest payload, map headers = {}) returns ListUpdateResponse|error { 238 | string resourcePath = string `/lists/${getEncodedUri(id)}`; 239 | http:Request request = new; 240 | json jsonBody = jsondata:toJson(payload); 241 | request.setPayload(jsonBody, "application/json"); 242 | return self.clientEp->put(resourcePath, request, headers); 243 | } 244 | 245 | # Delete List 246 | # 247 | # + id - The ID of the List to delete 248 | # + headers - Headers to be sent with the request 249 | # + return - The request has succeeded 250 | resource isolated function delete lists/[ListId id](map headers = {}) returns ListDeleteResponse|error { 251 | string resourcePath = string `/lists/${getEncodedUri(id)}`; 252 | return self.clientEp->delete(resourcePath, headers = headers); 253 | } 254 | 255 | # Returns User objects that follow a List by the provided List ID 256 | # 257 | # + id - The ID of the List 258 | # + headers - Headers to be sent with the request 259 | # + queries - Queries to be sent with the request 260 | # + return - The request has succeeded 261 | resource isolated function get lists/[ListId id]/followers(map headers = {}, *ListGetFollowersQueries queries) returns Get2ListsIdFollowersResponse|error { 262 | string resourcePath = string `/lists/${getEncodedUri(id)}/followers`; 263 | map queryParamEncoding = {"user.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "tweet.fields": {style: FORM, explode: false}}; 264 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 265 | return self.clientEp->get(resourcePath, headers); 266 | } 267 | 268 | # Returns User objects that are members of a List by the provided List ID. 269 | # 270 | # + id - The ID of the List 271 | # + headers - Headers to be sent with the request 272 | # + queries - Queries to be sent with the request 273 | # + return - The request has succeeded 274 | resource isolated function get lists/[ListId id]/members(map headers = {}, *ListGetMembersQueries queries) returns Get2ListsIdMembersResponse|error { 275 | string resourcePath = string `/lists/${getEncodedUri(id)}/members`; 276 | map queryParamEncoding = {"user.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "tweet.fields": {style: FORM, explode: false}}; 277 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 278 | return self.clientEp->get(resourcePath, headers); 279 | } 280 | 281 | # Add a List member 282 | # 283 | # + id - The ID of the List for which to add a member 284 | # + headers - Headers to be sent with the request 285 | # + return - The request has succeeded 286 | resource isolated function post lists/[ListId id]/members(ListAddUserRequest payload, map headers = {}) returns ListMutateResponse|error { 287 | string resourcePath = string `/lists/${getEncodedUri(id)}/members`; 288 | http:Request request = new; 289 | json jsonBody = jsondata:toJson(payload); 290 | request.setPayload(jsonBody, "application/json"); 291 | return self.clientEp->post(resourcePath, request, headers); 292 | } 293 | 294 | # Remove a List member 295 | # 296 | # + id - The ID of the List to remove a member 297 | # + userId - The ID of User that will be removed from the List 298 | # + headers - Headers to be sent with the request 299 | # + return - The request has succeeded 300 | resource isolated function delete lists/[ListId id]/members/[UserId userId](map headers = {}) returns ListMutateResponse|error { 301 | string resourcePath = string `/lists/${getEncodedUri(id)}/members/${getEncodedUri(userId)}`; 302 | return self.clientEp->delete(resourcePath, headers = headers); 303 | } 304 | 305 | # List Posts timeline by List ID. 306 | # 307 | # + id - The ID of the List 308 | # + headers - Headers to be sent with the request 309 | # + queries - Queries to be sent with the request 310 | # + return - The request has succeeded 311 | resource isolated function get lists/[ListId id]/tweets(map headers = {}, *ListsIdTweetsQueries queries) returns Get2ListsIdTweetsResponse|error { 312 | string resourcePath = string `/lists/${getEncodedUri(id)}/tweets`; 313 | map queryParamEncoding = {"tweet.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "media.fields": {style: FORM, explode: false}, "poll.fields": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "place.fields": {style: FORM, explode: false}}; 314 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 315 | return self.clientEp->get(resourcePath, headers); 316 | } 317 | 318 | # Returns the OpenAPI Specification document. 319 | # 320 | # + headers - Headers to be sent with the request 321 | # + return - The request was successful 322 | resource isolated function get openapi\.json(map headers = {}) returns record {}|error { 323 | string resourcePath = string `/openapi.json`; 324 | return self.clientEp->get(resourcePath, headers); 325 | } 326 | 327 | # Space lookup up Space IDs 328 | # 329 | # + headers - Headers to be sent with the request 330 | # + queries - Queries to be sent with the request 331 | # + return - The request has succeeded 332 | resource isolated function get spaces(map headers = {}, *FindSpacesByIdsQueries queries) returns Get2SpacesResponse|error { 333 | string resourcePath = string `/spaces`; 334 | map queryParamEncoding = {"ids": {style: FORM, explode: true}, "space.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "topic.fields": {style: FORM, explode: false}}; 335 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 336 | return self.clientEp->get(resourcePath, headers); 337 | } 338 | 339 | # Space lookup by their creators 340 | # 341 | # + headers - Headers to be sent with the request 342 | # + queries - Queries to be sent with the request 343 | # + return - The request has succeeded 344 | resource isolated function get spaces/'by/creator_ids(map headers = {}, *FindSpacesByCreatorIdsQueries queries) returns Get2SpacesByCreatorIdsResponse|error { 345 | string resourcePath = string `/spaces/by/creator_ids`; 346 | map queryParamEncoding = {"user_ids": {style: FORM, explode: true}, "space.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "topic.fields": {style: FORM, explode: false}}; 347 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 348 | return self.clientEp->get(resourcePath, headers); 349 | } 350 | 351 | # Search for Spaces 352 | # 353 | # + headers - Headers to be sent with the request 354 | # + queries - Queries to be sent with the request 355 | # + return - The request has succeeded 356 | resource isolated function get spaces/search(map headers = {}, *SearchSpacesQueries queries) returns Get2SpacesSearchResponse|error { 357 | string resourcePath = string `/spaces/search`; 358 | map queryParamEncoding = {"space.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "topic.fields": {style: FORM, explode: false}}; 359 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 360 | return self.clientEp->get(resourcePath, headers); 361 | } 362 | 363 | # Space lookup by Space ID 364 | # 365 | # + id - The ID of the Space to be retrieved 366 | # + headers - Headers to be sent with the request 367 | # + queries - Queries to be sent with the request 368 | # + return - The request has succeeded 369 | resource isolated function get spaces/[string id](map headers = {}, *FindSpaceByIdQueries queries) returns Get2SpacesIdResponse|error { 370 | string resourcePath = string `/spaces/${getEncodedUri(id)}`; 371 | map queryParamEncoding = {"space.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "topic.fields": {style: FORM, explode: false}}; 372 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 373 | return self.clientEp->get(resourcePath, headers); 374 | } 375 | 376 | # Retrieve the list of Users who purchased a ticket to the given space 377 | # 378 | # + id - The ID of the Space to be retrieved 379 | # + headers - Headers to be sent with the request 380 | # + queries - Queries to be sent with the request 381 | # + return - The request has succeeded 382 | resource isolated function get spaces/[string id]/buyers(map headers = {}, *SpaceBuyersQueries queries) returns Get2SpacesIdBuyersResponse|error { 383 | string resourcePath = string `/spaces/${getEncodedUri(id)}/buyers`; 384 | map queryParamEncoding = {"user.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "tweet.fields": {style: FORM, explode: false}}; 385 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 386 | return self.clientEp->get(resourcePath, headers); 387 | } 388 | 389 | # Retrieve Posts from a Space. 390 | # 391 | # + id - The ID of the Space to be retrieved 392 | # + headers - Headers to be sent with the request 393 | # + queries - Queries to be sent with the request 394 | # + return - The request has succeeded 395 | resource isolated function get spaces/[string id]/tweets(map headers = {}, *SpaceTweetsQueries queries) returns Get2SpacesIdTweetsResponse|error { 396 | string resourcePath = string `/spaces/${getEncodedUri(id)}/tweets`; 397 | map queryParamEncoding = {"tweet.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "media.fields": {style: FORM, explode: false}, "poll.fields": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "place.fields": {style: FORM, explode: false}}; 398 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 399 | return self.clientEp->get(resourcePath, headers); 400 | } 401 | 402 | # Trends 403 | # 404 | # + woeid - The WOEID of the place to lookup a trend for 405 | # + headers - Headers to be sent with the request 406 | # + queries - Queries to be sent with the request 407 | # + return - The request has succeeded 408 | resource isolated function get trends/'by/woeid/[int:Signed32 woeid](map headers = {}, *GetTrendsQueries queries) returns Get2TrendsByWoeidWoeidResponse|error { 409 | string resourcePath = string `/trends/by/woeid/${getEncodedUri(woeid)}`; 410 | map queryParamEncoding = {"trend.fields": {style: FORM, explode: false}}; 411 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 412 | return self.clientEp->get(resourcePath, headers); 413 | } 414 | 415 | # Post lookup by Post IDs 416 | # 417 | # + headers - Headers to be sent with the request 418 | # + queries - Queries to be sent with the request 419 | # + return - The request has succeeded 420 | resource isolated function get tweets(map headers = {}, *FindTweetsByIdQueries queries) returns Get2TweetsResponse|error { 421 | string resourcePath = string `/tweets`; 422 | map queryParamEncoding = {"ids": {style: FORM, explode: false}, "tweet.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "media.fields": {style: FORM, explode: false}, "poll.fields": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "place.fields": {style: FORM, explode: false}}; 423 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 424 | return self.clientEp->get(resourcePath, headers); 425 | } 426 | 427 | # Creation of a Post 428 | # 429 | # + headers - Headers to be sent with the request 430 | # + return - The request has succeeded 431 | resource isolated function post tweets(TweetCreateRequest payload, map headers = {}) returns TweetCreateResponse|error { 432 | string resourcePath = string `/tweets`; 433 | http:Request request = new; 434 | json jsonBody = jsondata:toJson(payload); 435 | request.setPayload(jsonBody, "application/json"); 436 | return self.clientEp->post(resourcePath, request, headers); 437 | } 438 | 439 | # Posts Compliance stream 440 | # 441 | # + headers - Headers to be sent with the request 442 | # + queries - Queries to be sent with the request 443 | # + return - The request has succeeded 444 | resource isolated function get tweets/compliance/'stream(map headers = {}, *GetTweetsComplianceStreamQueries queries) returns TweetComplianceStreamResponse|error { 445 | string resourcePath = string `/tweets/compliance/stream`; 446 | resourcePath = resourcePath + check getPathForQueryParam(queries); 447 | return self.clientEp->get(resourcePath, headers); 448 | } 449 | 450 | # Full archive search counts 451 | # 452 | # + headers - Headers to be sent with the request 453 | # + queries - Queries to be sent with the request 454 | # + return - The request has succeeded 455 | resource isolated function get tweets/counts/all(map headers = {}, *TweetCountsFullArchiveSearchQueries queries) returns Get2TweetsCountsAllResponse|error { 456 | string resourcePath = string `/tweets/counts/all`; 457 | map queryParamEncoding = {"search_count.fields": {style: FORM, explode: false}}; 458 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 459 | return self.clientEp->get(resourcePath, headers); 460 | } 461 | 462 | # Recent search counts 463 | # 464 | # + headers - Headers to be sent with the request 465 | # + queries - Queries to be sent with the request 466 | # + return - The request has succeeded 467 | resource isolated function get tweets/counts/recent(map headers = {}, *TweetCountsRecentSearchQueries queries) returns Get2TweetsCountsRecentResponse|error { 468 | string resourcePath = string `/tweets/counts/recent`; 469 | map queryParamEncoding = {"search_count.fields": {style: FORM, explode: false}}; 470 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 471 | return self.clientEp->get(resourcePath, headers); 472 | } 473 | 474 | # Firehose stream 475 | # 476 | # + headers - Headers to be sent with the request 477 | # + queries - Queries to be sent with the request 478 | # + return - The request has succeeded 479 | resource isolated function get tweets/firehose/'stream(map headers = {}, *GetTweetsFirehoseStreamQueries queries) returns StreamingTweetResponse|error { 480 | string resourcePath = string `/tweets/firehose/stream`; 481 | map queryParamEncoding = {"tweet.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "media.fields": {style: FORM, explode: false}, "poll.fields": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "place.fields": {style: FORM, explode: false}}; 482 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 483 | return self.clientEp->get(resourcePath, headers); 484 | } 485 | 486 | # English Language Firehose stream 487 | # 488 | # + headers - Headers to be sent with the request 489 | # + queries - Queries to be sent with the request 490 | # + return - The request has succeeded 491 | resource isolated function get tweets/firehose/'stream/lang/en(map headers = {}, *GetTweetsFirehoseStreamLangEnQueries queries) returns StreamingTweetResponse|error { 492 | string resourcePath = string `/tweets/firehose/stream/lang/en`; 493 | map queryParamEncoding = {"tweet.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "media.fields": {style: FORM, explode: false}, "poll.fields": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "place.fields": {style: FORM, explode: false}}; 494 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 495 | return self.clientEp->get(resourcePath, headers); 496 | } 497 | 498 | # Japanese Language Firehose stream 499 | # 500 | # + headers - Headers to be sent with the request 501 | # + queries - Queries to be sent with the request 502 | # + return - The request has succeeded 503 | resource isolated function get tweets/firehose/'stream/lang/ja(map headers = {}, *GetTweetsFirehoseStreamLangJaQueries queries) returns StreamingTweetResponse|error { 504 | string resourcePath = string `/tweets/firehose/stream/lang/ja`; 505 | map queryParamEncoding = {"tweet.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "media.fields": {style: FORM, explode: false}, "poll.fields": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "place.fields": {style: FORM, explode: false}}; 506 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 507 | return self.clientEp->get(resourcePath, headers); 508 | } 509 | 510 | # Korean Language Firehose stream 511 | # 512 | # + headers - Headers to be sent with the request 513 | # + queries - Queries to be sent with the request 514 | # + return - The request has succeeded 515 | resource isolated function get tweets/firehose/'stream/lang/ko(map headers = {}, *GetTweetsFirehoseStreamLangKoQueries queries) returns StreamingTweetResponse|error { 516 | string resourcePath = string `/tweets/firehose/stream/lang/ko`; 517 | map queryParamEncoding = {"tweet.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "media.fields": {style: FORM, explode: false}, "poll.fields": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "place.fields": {style: FORM, explode: false}}; 518 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 519 | return self.clientEp->get(resourcePath, headers); 520 | } 521 | 522 | # Portuguese Language Firehose stream 523 | # 524 | # + headers - Headers to be sent with the request 525 | # + queries - Queries to be sent with the request 526 | # + return - The request has succeeded 527 | resource isolated function get tweets/firehose/'stream/lang/pt(map headers = {}, *GetTweetsFirehoseStreamLangPtQueries queries) returns StreamingTweetResponse|error { 528 | string resourcePath = string `/tweets/firehose/stream/lang/pt`; 529 | map queryParamEncoding = {"tweet.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "media.fields": {style: FORM, explode: false}, "poll.fields": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "place.fields": {style: FORM, explode: false}}; 530 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 531 | return self.clientEp->get(resourcePath, headers); 532 | } 533 | 534 | # Posts Label stream 535 | # 536 | # + headers - Headers to be sent with the request 537 | # + queries - Queries to be sent with the request 538 | # + return - The request has succeeded 539 | resource isolated function get tweets/label/'stream(map headers = {}, *GetTweetsLabelStreamQueries queries) returns TweetLabelStreamResponse|error { 540 | string resourcePath = string `/tweets/label/stream`; 541 | resourcePath = resourcePath + check getPathForQueryParam(queries); 542 | return self.clientEp->get(resourcePath, headers); 543 | } 544 | 545 | # Sample stream 546 | # 547 | # + headers - Headers to be sent with the request 548 | # + queries - Queries to be sent with the request 549 | # + return - The request has succeeded 550 | resource isolated function get tweets/sample/'stream(map headers = {}, *SampleStreamQueries queries) returns StreamingTweetResponse|error { 551 | string resourcePath = string `/tweets/sample/stream`; 552 | map queryParamEncoding = {"tweet.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "media.fields": {style: FORM, explode: false}, "poll.fields": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "place.fields": {style: FORM, explode: false}}; 553 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 554 | return self.clientEp->get(resourcePath, headers); 555 | } 556 | 557 | # Sample 10% stream 558 | # 559 | # + headers - Headers to be sent with the request 560 | # + queries - Queries to be sent with the request 561 | # + return - The request has succeeded 562 | resource isolated function get tweets/sample10/'stream(map headers = {}, *GetTweetsSample10StreamQueries queries) returns Get2TweetsSample10StreamResponse|error { 563 | string resourcePath = string `/tweets/sample10/stream`; 564 | map queryParamEncoding = {"tweet.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "media.fields": {style: FORM, explode: false}, "poll.fields": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "place.fields": {style: FORM, explode: false}}; 565 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 566 | return self.clientEp->get(resourcePath, headers); 567 | } 568 | 569 | # Full-archive search 570 | # 571 | # + headers - Headers to be sent with the request 572 | # + queries - Queries to be sent with the request 573 | # + return - The request has succeeded 574 | resource isolated function get tweets/search/all(map headers = {}, *TweetsFullarchiveSearchQueries queries) returns Get2TweetsSearchAllResponse|error { 575 | string resourcePath = string `/tweets/search/all`; 576 | map queryParamEncoding = {"tweet.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "media.fields": {style: FORM, explode: false}, "poll.fields": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "place.fields": {style: FORM, explode: false}}; 577 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 578 | return self.clientEp->get(resourcePath, headers); 579 | } 580 | 581 | # Recent search 582 | # 583 | # + headers - Headers to be sent with the request 584 | # + queries - Queries to be sent with the request 585 | # + return - The request has succeeded 586 | resource isolated function get tweets/search/recent(map headers = {}, *TweetsRecentSearchQueries queries) returns Get2TweetsSearchRecentResponse|error { 587 | string resourcePath = string `/tweets/search/recent`; 588 | map queryParamEncoding = {"tweet.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "media.fields": {style: FORM, explode: false}, "poll.fields": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "place.fields": {style: FORM, explode: false}}; 589 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 590 | return self.clientEp->get(resourcePath, headers); 591 | } 592 | 593 | # Filtered stream 594 | # 595 | # + headers - Headers to be sent with the request 596 | # + queries - Queries to be sent with the request 597 | # + return - The request has succeeded 598 | resource isolated function get tweets/search/'stream(map headers = {}, *SearchStreamQueries queries) returns FilteredStreamingTweetResponse|error { 599 | string resourcePath = string `/tweets/search/stream`; 600 | map queryParamEncoding = {"tweet.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "media.fields": {style: FORM, explode: false}, "poll.fields": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "place.fields": {style: FORM, explode: false}}; 601 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 602 | return self.clientEp->get(resourcePath, headers); 603 | } 604 | 605 | # Rules lookup 606 | # 607 | # + headers - Headers to be sent with the request 608 | # + queries - Queries to be sent with the request 609 | # + return - The request has succeeded 610 | resource isolated function get tweets/search/'stream/rules(map headers = {}, *GetRulesQueries queries) returns RulesLookupResponse|error { 611 | string resourcePath = string `/tweets/search/stream/rules`; 612 | map queryParamEncoding = {"ids": {style: FORM, explode: true}}; 613 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 614 | return self.clientEp->get(resourcePath, headers); 615 | } 616 | 617 | # Add/Delete rules 618 | # 619 | # + headers - Headers to be sent with the request 620 | # + queries - Queries to be sent with the request 621 | # + return - The request has succeeded 622 | resource isolated function post tweets/search/'stream/rules(AddOrDeleteRulesRequest payload, map headers = {}, *AddOrDeleteRulesQueries queries) returns AddOrDeleteRulesResponse|error { 623 | string resourcePath = string `/tweets/search/stream/rules`; 624 | resourcePath = resourcePath + check getPathForQueryParam(queries); 625 | http:Request request = new; 626 | json jsonBody = jsondata:toJson(payload); 627 | request.setPayload(jsonBody, "application/json"); 628 | return self.clientEp->post(resourcePath, request, headers); 629 | } 630 | 631 | # Rules Count 632 | # 633 | # + headers - Headers to be sent with the request 634 | # + queries - Queries to be sent with the request 635 | # + return - The request has succeeded 636 | resource isolated function get tweets/search/'stream/rules/counts(map headers = {}, *GetRuleCountQueries queries) returns Get2TweetsSearchStreamRulesCountsResponse|error { 637 | string resourcePath = string `/tweets/search/stream/rules/counts`; 638 | map queryParamEncoding = {"rules_count.fields": {style: FORM, explode: false}}; 639 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 640 | return self.clientEp->get(resourcePath, headers); 641 | } 642 | 643 | # Post lookup by Post ID 644 | # 645 | # + id - A single Post ID 646 | # + headers - Headers to be sent with the request 647 | # + queries - Queries to be sent with the request 648 | # + return - The request has succeeded 649 | resource isolated function get tweets/[TweetId id](map headers = {}, *FindTweetByIdQueries queries) returns Get2TweetsIdResponse|error { 650 | string resourcePath = string `/tweets/${getEncodedUri(id)}`; 651 | map queryParamEncoding = {"tweet.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "media.fields": {style: FORM, explode: false}, "poll.fields": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "place.fields": {style: FORM, explode: false}}; 652 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 653 | return self.clientEp->get(resourcePath, headers); 654 | } 655 | 656 | # Post delete by Post ID 657 | # 658 | # + id - The ID of the Post to be deleted 659 | # + headers - Headers to be sent with the request 660 | # + return - The request has succeeded 661 | resource isolated function delete tweets/[TweetId id](map headers = {}) returns TweetDeleteResponse|error { 662 | string resourcePath = string `/tweets/${getEncodedUri(id)}`; 663 | return self.clientEp->delete(resourcePath, headers = headers); 664 | } 665 | 666 | # Returns User objects that have liked the provided Post ID 667 | # 668 | # + id - A single Post ID 669 | # + headers - Headers to be sent with the request 670 | # + queries - Queries to be sent with the request 671 | # + return - The request has succeeded 672 | resource isolated function get tweets/[TweetId id]/liking_users(map headers = {}, *TweetsIdLikingUsersQueries queries) returns Get2TweetsIdLikingUsersResponse|error { 673 | string resourcePath = string `/tweets/${getEncodedUri(id)}/liking_users`; 674 | map queryParamEncoding = {"user.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "tweet.fields": {style: FORM, explode: false}}; 675 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 676 | return self.clientEp->get(resourcePath, headers); 677 | } 678 | 679 | # Retrieve Posts that quote a Post. 680 | # 681 | # + id - A single Post ID 682 | # + headers - Headers to be sent with the request 683 | # + queries - Queries to be sent with the request 684 | # + return - The request has succeeded 685 | resource isolated function get tweets/[TweetId id]/quote_tweets(map headers = {}, *FindTweetsThatQuoteATweetQueries queries) returns Get2TweetsIdQuoteTweetsResponse|error { 686 | string resourcePath = string `/tweets/${getEncodedUri(id)}/quote_tweets`; 687 | map queryParamEncoding = {"exclude": {style: FORM, explode: false}, "tweet.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "media.fields": {style: FORM, explode: false}, "poll.fields": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "place.fields": {style: FORM, explode: false}}; 688 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 689 | return self.clientEp->get(resourcePath, headers); 690 | } 691 | 692 | # Returns User objects that have retweeted the provided Post ID 693 | # 694 | # + id - A single Post ID 695 | # + headers - Headers to be sent with the request 696 | # + queries - Queries to be sent with the request 697 | # + return - The request has succeeded 698 | resource isolated function get tweets/[TweetId id]/retweeted_by(map headers = {}, *TweetsIdRetweetingUsersQueries queries) returns Get2TweetsIdRetweetedByResponse|error { 699 | string resourcePath = string `/tweets/${getEncodedUri(id)}/retweeted_by`; 700 | map queryParamEncoding = {"user.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "tweet.fields": {style: FORM, explode: false}}; 701 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 702 | return self.clientEp->get(resourcePath, headers); 703 | } 704 | 705 | # Retrieve Posts that repost a Post. 706 | # 707 | # + id - A single Post ID 708 | # + headers - Headers to be sent with the request 709 | # + queries - Queries to be sent with the request 710 | # + return - The request has succeeded 711 | resource isolated function get tweets/[TweetId id]/retweets(map headers = {}, *FindTweetsThatRetweetATweetQueries queries) returns Get2TweetsIdRetweetsResponse|error { 712 | string resourcePath = string `/tweets/${getEncodedUri(id)}/retweets`; 713 | map queryParamEncoding = {"tweet.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "media.fields": {style: FORM, explode: false}, "poll.fields": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "place.fields": {style: FORM, explode: false}}; 714 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 715 | return self.clientEp->get(resourcePath, headers); 716 | } 717 | 718 | # Hide replies 719 | # 720 | # + tweetId - The ID of the reply that you want to hide or unhide 721 | # + headers - Headers to be sent with the request 722 | # + return - The request has succeeded 723 | resource isolated function put tweets/[TweetId tweetId]/hidden(TweetHideRequest payload, map headers = {}) returns TweetHideResponse|error { 724 | string resourcePath = string `/tweets/${getEncodedUri(tweetId)}/hidden`; 725 | http:Request request = new; 726 | json jsonBody = jsondata:toJson(payload); 727 | request.setPayload(jsonBody, "application/json"); 728 | return self.clientEp->put(resourcePath, request, headers); 729 | } 730 | 731 | # Post Usage 732 | # 733 | # + headers - Headers to be sent with the request 734 | # + queries - Queries to be sent with the request 735 | # + return - The request has succeeded 736 | resource isolated function get usage/tweets(map headers = {}, *GetUsageTweetsQueries queries) returns Get2UsageTweetsResponse|error { 737 | string resourcePath = string `/usage/tweets`; 738 | map queryParamEncoding = {"usage.fields": {style: FORM, explode: false}}; 739 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 740 | return self.clientEp->get(resourcePath, headers); 741 | } 742 | 743 | # User lookup by IDs 744 | # 745 | # + headers - Headers to be sent with the request 746 | # + queries - Queries to be sent with the request 747 | # + return - The request has succeeded 748 | resource isolated function get users(map headers = {}, *FindUsersByIdQueries queries) returns Get2UsersResponse|error { 749 | string resourcePath = string `/users`; 750 | map queryParamEncoding = {"ids": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "tweet.fields": {style: FORM, explode: false}}; 751 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 752 | return self.clientEp->get(resourcePath, headers); 753 | } 754 | 755 | # User lookup by usernames 756 | # 757 | # + headers - Headers to be sent with the request 758 | # + queries - Queries to be sent with the request 759 | # + return - The request has succeeded 760 | resource isolated function get users/'by(map headers = {}, *FindUsersByUsernameQueries queries) returns Get2UsersByResponse|error { 761 | string resourcePath = string `/users/by`; 762 | map queryParamEncoding = {"usernames": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "tweet.fields": {style: FORM, explode: false}}; 763 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 764 | return self.clientEp->get(resourcePath, headers); 765 | } 766 | 767 | # User lookup by username 768 | # 769 | # + username - A username 770 | # + headers - Headers to be sent with the request 771 | # + queries - Queries to be sent with the request 772 | # + return - The request has succeeded 773 | resource isolated function get users/'by/username/[string username](map headers = {}, *FindUserByUsernameQueries queries) returns Get2UsersByUsernameUsernameResponse|error { 774 | string resourcePath = string `/users/by/username/${getEncodedUri(username)}`; 775 | map queryParamEncoding = {"user.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "tweet.fields": {style: FORM, explode: false}}; 776 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 777 | return self.clientEp->get(resourcePath, headers); 778 | } 779 | 780 | # Users Compliance stream 781 | # 782 | # + headers - Headers to be sent with the request 783 | # + queries - Queries to be sent with the request 784 | # + return - The request has succeeded 785 | resource isolated function get users/compliance/'stream(map headers = {}, *GetUsersComplianceStreamQueries queries) returns UserComplianceStreamResponse|error { 786 | string resourcePath = string `/users/compliance/stream`; 787 | resourcePath = resourcePath + check getPathForQueryParam(queries); 788 | return self.clientEp->get(resourcePath, headers); 789 | } 790 | 791 | # User lookup me 792 | # 793 | # + headers - Headers to be sent with the request 794 | # + queries - Queries to be sent with the request 795 | # + return - The request has succeeded 796 | resource isolated function get users/me(map headers = {}, *FindMyUserQueries queries) returns Get2UsersMeResponse|error { 797 | string resourcePath = string `/users/me`; 798 | map queryParamEncoding = {"user.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "tweet.fields": {style: FORM, explode: false}}; 799 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 800 | return self.clientEp->get(resourcePath, headers); 801 | } 802 | 803 | # User search 804 | # 805 | # + headers - Headers to be sent with the request 806 | # + queries - Queries to be sent with the request 807 | # + return - The request has succeeded 808 | resource isolated function get users/search(map headers = {}, *SearchUserByQueryQueries queries) returns Get2UsersSearchResponse|error { 809 | string resourcePath = string `/users/search`; 810 | map queryParamEncoding = {"user.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "tweet.fields": {style: FORM, explode: false}}; 811 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 812 | return self.clientEp->get(resourcePath, headers); 813 | } 814 | 815 | # User lookup by ID 816 | # 817 | # + id - The ID of the User to lookup 818 | # + headers - Headers to be sent with the request 819 | # + queries - Queries to be sent with the request 820 | # + return - The request has succeeded 821 | resource isolated function get users/[UserId id](map headers = {}, *FindUserByIdQueries queries) returns Get2UsersIdResponse|error { 822 | string resourcePath = string `/users/${getEncodedUri(id)}`; 823 | map queryParamEncoding = {"user.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "tweet.fields": {style: FORM, explode: false}}; 824 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 825 | return self.clientEp->get(resourcePath, headers); 826 | } 827 | 828 | # Returns User objects that are blocked by provided User ID 829 | # 830 | # + id - The ID of the authenticated source User for whom to return results 831 | # + headers - Headers to be sent with the request 832 | # + queries - Queries to be sent with the request 833 | # + return - The request has succeeded 834 | resource isolated function get users/[UserIdMatchesAuthenticatedUser id]/blocking(map headers = {}, *UsersIdBlockingQueries queries) returns Get2UsersIdBlockingResponse|error { 835 | string resourcePath = string `/users/${getEncodedUri(id)}/blocking`; 836 | map queryParamEncoding = {"user.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "tweet.fields": {style: FORM, explode: false}}; 837 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 838 | return self.clientEp->get(resourcePath, headers); 839 | } 840 | 841 | # Bookmarks by User 842 | # 843 | # + id - The ID of the authenticated source User for whom to return results 844 | # + headers - Headers to be sent with the request 845 | # + queries - Queries to be sent with the request 846 | # + return - The request has succeeded 847 | resource isolated function get users/[UserIdMatchesAuthenticatedUser id]/bookmarks(map headers = {}, *GetUsersIdBookmarksQueries queries) returns Get2UsersIdBookmarksResponse|error { 848 | string resourcePath = string `/users/${getEncodedUri(id)}/bookmarks`; 849 | map queryParamEncoding = {"tweet.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "media.fields": {style: FORM, explode: false}, "poll.fields": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "place.fields": {style: FORM, explode: false}}; 850 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 851 | return self.clientEp->get(resourcePath, headers); 852 | } 853 | 854 | # Add Post to Bookmarks 855 | # 856 | # + id - The ID of the authenticated source User for whom to add bookmarks 857 | # + headers - Headers to be sent with the request 858 | # + return - The request has succeeded 859 | resource isolated function post users/[UserIdMatchesAuthenticatedUser id]/bookmarks(BookmarkAddRequest payload, map headers = {}) returns BookmarkMutationResponse|error { 860 | string resourcePath = string `/users/${getEncodedUri(id)}/bookmarks`; 861 | http:Request request = new; 862 | json jsonBody = jsondata:toJson(payload); 863 | request.setPayload(jsonBody, "application/json"); 864 | return self.clientEp->post(resourcePath, request, headers); 865 | } 866 | 867 | # Remove a bookmarked Post 868 | # 869 | # + id - The ID of the authenticated source User whose bookmark is to be removed 870 | # + tweetId - The ID of the Post that the source User is removing from bookmarks 871 | # + headers - Headers to be sent with the request 872 | # + return - The request has succeeded 873 | resource isolated function delete users/[UserIdMatchesAuthenticatedUser id]/bookmarks/[TweetId tweetId](map headers = {}) returns BookmarkMutationResponse|error { 874 | string resourcePath = string `/users/${getEncodedUri(id)}/bookmarks/${getEncodedUri(tweetId)}`; 875 | return self.clientEp->delete(resourcePath, headers = headers); 876 | } 877 | 878 | # Get User's Followed Lists 879 | # 880 | # + id - The ID of the User to lookup 881 | # + headers - Headers to be sent with the request 882 | # + queries - Queries to be sent with the request 883 | # + return - The request has succeeded 884 | resource isolated function get users/[UserId id]/followed_lists(map headers = {}, *UserFollowedListsQueries queries) returns Get2UsersIdFollowedListsResponse|error { 885 | string resourcePath = string `/users/${getEncodedUri(id)}/followed_lists`; 886 | map queryParamEncoding = {"list.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}}; 887 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 888 | return self.clientEp->get(resourcePath, headers); 889 | } 890 | 891 | # Follow a List 892 | # 893 | # + id - The ID of the authenticated source User that will follow the List 894 | # + headers - Headers to be sent with the request 895 | # + return - The request has succeeded 896 | resource isolated function post users/[UserIdMatchesAuthenticatedUser id]/followed_lists(ListFollowedRequest payload, map headers = {}) returns ListFollowedResponse|error { 897 | string resourcePath = string `/users/${getEncodedUri(id)}/followed_lists`; 898 | http:Request request = new; 899 | json jsonBody = jsondata:toJson(payload); 900 | request.setPayload(jsonBody, "application/json"); 901 | return self.clientEp->post(resourcePath, request, headers); 902 | } 903 | 904 | # Unfollow a List 905 | # 906 | # + id - The ID of the authenticated source User that will unfollow the List 907 | # + listId - The ID of the List to unfollow 908 | # + headers - Headers to be sent with the request 909 | # + return - The request has succeeded 910 | resource isolated function delete users/[UserIdMatchesAuthenticatedUser id]/followed_lists/[ListId listId](map headers = {}) returns ListFollowedResponse|error { 911 | string resourcePath = string `/users/${getEncodedUri(id)}/followed_lists/${getEncodedUri(listId)}`; 912 | return self.clientEp->delete(resourcePath, headers = headers); 913 | } 914 | 915 | # Followers by User ID 916 | # 917 | # + id - The ID of the User to lookup 918 | # + headers - Headers to be sent with the request 919 | # + queries - Queries to be sent with the request 920 | # + return - The request has succeeded 921 | resource isolated function get users/[UserId id]/followers(map headers = {}, *UsersIdFollowersQueries queries) returns Get2UsersIdFollowersResponse|error { 922 | string resourcePath = string `/users/${getEncodedUri(id)}/followers`; 923 | map queryParamEncoding = {"user.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "tweet.fields": {style: FORM, explode: false}}; 924 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 925 | return self.clientEp->get(resourcePath, headers); 926 | } 927 | 928 | # Following by User ID 929 | # 930 | # + id - The ID of the User to lookup 931 | # + headers - Headers to be sent with the request 932 | # + queries - Queries to be sent with the request 933 | # + return - The request has succeeded 934 | resource isolated function get users/[UserId id]/following(map headers = {}, *UsersIdFollowingQueries queries) returns Get2UsersIdFollowingResponse|error { 935 | string resourcePath = string `/users/${getEncodedUri(id)}/following`; 936 | map queryParamEncoding = {"user.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "tweet.fields": {style: FORM, explode: false}}; 937 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 938 | return self.clientEp->get(resourcePath, headers); 939 | } 940 | 941 | # Follow User 942 | # 943 | # + id - The ID of the authenticated source User that is requesting to follow the target User 944 | # + headers - Headers to be sent with the request 945 | # + return - The request has succeeded 946 | resource isolated function post users/[UserIdMatchesAuthenticatedUser id]/following(UsersFollowingCreateRequest payload, map headers = {}) returns UsersFollowingCreateResponse|error { 947 | string resourcePath = string `/users/${getEncodedUri(id)}/following`; 948 | http:Request request = new; 949 | json jsonBody = jsondata:toJson(payload); 950 | request.setPayload(jsonBody, "application/json"); 951 | return self.clientEp->post(resourcePath, request, headers); 952 | } 953 | 954 | # Returns Post objects liked by the provided User ID 955 | # 956 | # + id - The ID of the User to lookup 957 | # + headers - Headers to be sent with the request 958 | # + queries - Queries to be sent with the request 959 | # + return - The request has succeeded 960 | resource isolated function get users/[UserId id]/liked_tweets(map headers = {}, *UsersIdLikedTweetsQueries queries) returns Get2UsersIdLikedTweetsResponse|error { 961 | string resourcePath = string `/users/${getEncodedUri(id)}/liked_tweets`; 962 | map queryParamEncoding = {"tweet.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "media.fields": {style: FORM, explode: false}, "poll.fields": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "place.fields": {style: FORM, explode: false}}; 963 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 964 | return self.clientEp->get(resourcePath, headers); 965 | } 966 | 967 | # Causes the User (in the path) to like the specified Post 968 | # 969 | # + id - The ID of the authenticated source User that is requesting to like the Post 970 | # + headers - Headers to be sent with the request 971 | # + return - The request has succeeded 972 | resource isolated function post users/[UserIdMatchesAuthenticatedUser id]/likes(UsersLikesCreateRequest payload, map headers = {}) returns UsersLikesCreateResponse|error { 973 | string resourcePath = string `/users/${getEncodedUri(id)}/likes`; 974 | http:Request request = new; 975 | json jsonBody = jsondata:toJson(payload); 976 | request.setPayload(jsonBody, "application/json"); 977 | return self.clientEp->post(resourcePath, request, headers); 978 | } 979 | 980 | # Causes the User (in the path) to unlike the specified Post 981 | # 982 | # + id - The ID of the authenticated source User that is requesting to unlike the Post 983 | # + tweetId - The ID of the Post that the User is requesting to unlike 984 | # + headers - Headers to be sent with the request 985 | # + return - The request has succeeded 986 | resource isolated function delete users/[UserIdMatchesAuthenticatedUser id]/likes/[TweetId tweetId](map headers = {}) returns UsersLikesDeleteResponse|error { 987 | string resourcePath = string `/users/${getEncodedUri(id)}/likes/${getEncodedUri(tweetId)}`; 988 | return self.clientEp->delete(resourcePath, headers = headers); 989 | } 990 | 991 | # Get a User's List Memberships 992 | # 993 | # + id - The ID of the User to lookup 994 | # + headers - Headers to be sent with the request 995 | # + queries - Queries to be sent with the request 996 | # + return - The request has succeeded 997 | resource isolated function get users/[UserId id]/list_memberships(map headers = {}, *GetUserListMembershipsQueries queries) returns Get2UsersIdListMembershipsResponse|error { 998 | string resourcePath = string `/users/${getEncodedUri(id)}/list_memberships`; 999 | map queryParamEncoding = {"list.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}}; 1000 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 1001 | return self.clientEp->get(resourcePath, headers); 1002 | } 1003 | 1004 | # User mention timeline by User ID 1005 | # 1006 | # + id - The ID of the User to lookup 1007 | # + headers - Headers to be sent with the request 1008 | # + queries - Queries to be sent with the request 1009 | # + return - The request has succeeded 1010 | resource isolated function get users/[UserId id]/mentions(map headers = {}, *UsersIdMentionsQueries queries) returns Get2UsersIdMentionsResponse|error { 1011 | string resourcePath = string `/users/${getEncodedUri(id)}/mentions`; 1012 | map queryParamEncoding = {"tweet.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "media.fields": {style: FORM, explode: false}, "poll.fields": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "place.fields": {style: FORM, explode: false}}; 1013 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 1014 | return self.clientEp->get(resourcePath, headers); 1015 | } 1016 | 1017 | # Returns User objects that are muted by the provided User ID 1018 | # 1019 | # + id - The ID of the authenticated source User for whom to return results 1020 | # + headers - Headers to be sent with the request 1021 | # + queries - Queries to be sent with the request 1022 | # + return - The request has succeeded 1023 | resource isolated function get users/[UserIdMatchesAuthenticatedUser id]/muting(map headers = {}, *UsersIdMutingQueries queries) returns Get2UsersIdMutingResponse|error { 1024 | string resourcePath = string `/users/${getEncodedUri(id)}/muting`; 1025 | map queryParamEncoding = {"user.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "tweet.fields": {style: FORM, explode: false}}; 1026 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 1027 | return self.clientEp->get(resourcePath, headers); 1028 | } 1029 | 1030 | # Mute User by User ID. 1031 | # 1032 | # + id - The ID of the authenticated source User that is requesting to mute the target User 1033 | # + headers - Headers to be sent with the request 1034 | # + return - The request has succeeded 1035 | resource isolated function post users/[UserIdMatchesAuthenticatedUser id]/muting(MuteUserRequest payload, map headers = {}) returns MuteUserMutationResponse|error { 1036 | string resourcePath = string `/users/${getEncodedUri(id)}/muting`; 1037 | http:Request request = new; 1038 | json jsonBody = jsondata:toJson(payload); 1039 | request.setPayload(jsonBody, "application/json"); 1040 | return self.clientEp->post(resourcePath, request, headers); 1041 | } 1042 | 1043 | # Get a User's Owned Lists. 1044 | # 1045 | # + id - The ID of the User to lookup 1046 | # + headers - Headers to be sent with the request 1047 | # + queries - Queries to be sent with the request 1048 | # + return - The request has succeeded 1049 | resource isolated function get users/[UserId id]/owned_lists(map headers = {}, *ListUserOwnedListsQueries queries) returns Get2UsersIdOwnedListsResponse|error { 1050 | string resourcePath = string `/users/${getEncodedUri(id)}/owned_lists`; 1051 | map queryParamEncoding = {"list.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}}; 1052 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 1053 | return self.clientEp->get(resourcePath, headers); 1054 | } 1055 | 1056 | # Get a User's Pinned Lists 1057 | # 1058 | # + id - The ID of the authenticated source User for whom to return results 1059 | # + headers - Headers to be sent with the request 1060 | # + queries - Queries to be sent with the request 1061 | # + return - The request has succeeded 1062 | resource isolated function get users/[UserIdMatchesAuthenticatedUser id]/pinned_lists(map headers = {}, *ListUserPinnedListsQueries queries) returns Get2UsersIdPinnedListsResponse|error { 1063 | string resourcePath = string `/users/${getEncodedUri(id)}/pinned_lists`; 1064 | map queryParamEncoding = {"list.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}}; 1065 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 1066 | return self.clientEp->get(resourcePath, headers); 1067 | } 1068 | 1069 | # Pin a List 1070 | # 1071 | # + id - The ID of the authenticated source User that will pin the List 1072 | # + headers - Headers to be sent with the request 1073 | # + return - The request has succeeded 1074 | resource isolated function post users/[UserIdMatchesAuthenticatedUser id]/pinned_lists(ListPinnedRequest payload, map headers = {}) returns ListPinnedResponse|error { 1075 | string resourcePath = string `/users/${getEncodedUri(id)}/pinned_lists`; 1076 | http:Request request = new; 1077 | json jsonBody = jsondata:toJson(payload); 1078 | request.setPayload(jsonBody, "application/json"); 1079 | return self.clientEp->post(resourcePath, request, headers); 1080 | } 1081 | 1082 | # Unpin a List 1083 | # 1084 | # + id - The ID of the authenticated source User for whom to return results 1085 | # + listId - The ID of the List to unpin 1086 | # + headers - Headers to be sent with the request 1087 | # + return - The request has succeeded 1088 | resource isolated function delete users/[UserIdMatchesAuthenticatedUser id]/pinned_lists/[ListId listId](map headers = {}) returns ListUnpinResponse|error { 1089 | string resourcePath = string `/users/${getEncodedUri(id)}/pinned_lists/${getEncodedUri(listId)}`; 1090 | return self.clientEp->delete(resourcePath, headers = headers); 1091 | } 1092 | 1093 | # Causes the User (in the path) to repost the specified Post. 1094 | # 1095 | # + id - The ID of the authenticated source User that is requesting to repost the Post 1096 | # + headers - Headers to be sent with the request 1097 | # + return - The request has succeeded 1098 | resource isolated function post users/[UserIdMatchesAuthenticatedUser id]/retweets(UsersRetweetsCreateRequest payload, map headers = {}) returns UsersRetweetsCreateResponse|error { 1099 | string resourcePath = string `/users/${getEncodedUri(id)}/retweets`; 1100 | http:Request request = new; 1101 | json jsonBody = jsondata:toJson(payload); 1102 | request.setPayload(jsonBody, "application/json"); 1103 | return self.clientEp->post(resourcePath, request, headers); 1104 | } 1105 | 1106 | # Causes the User (in the path) to unretweet the specified Post 1107 | # 1108 | # + id - The ID of the authenticated source User that is requesting to repost the Post 1109 | # + sourceTweetId - The ID of the Post that the User is requesting to unretweet 1110 | # + headers - Headers to be sent with the request 1111 | # + return - The request has succeeded 1112 | resource isolated function delete users/[UserIdMatchesAuthenticatedUser id]/retweets/[TweetId sourceTweetId](map headers = {}) returns UsersRetweetsDeleteResponse|error { 1113 | string resourcePath = string `/users/${getEncodedUri(id)}/retweets/${getEncodedUri(sourceTweetId)}`; 1114 | return self.clientEp->delete(resourcePath, headers = headers); 1115 | } 1116 | 1117 | # User home timeline by User ID 1118 | # 1119 | # + id - The ID of the authenticated source User to list Reverse Chronological Timeline Posts of 1120 | # + headers - Headers to be sent with the request 1121 | # + queries - Queries to be sent with the request 1122 | # + return - The request has succeeded 1123 | resource isolated function get users/[UserIdMatchesAuthenticatedUser id]/timelines/reverse_chronological(map headers = {}, *UsersIdTimelineQueries queries) returns Get2UsersIdTimelinesReverseChronologicalResponse|error { 1124 | string resourcePath = string `/users/${getEncodedUri(id)}/timelines/reverse_chronological`; 1125 | map queryParamEncoding = {"exclude": {style: FORM, explode: false}, "tweet.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "media.fields": {style: FORM, explode: false}, "poll.fields": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "place.fields": {style: FORM, explode: false}}; 1126 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 1127 | return self.clientEp->get(resourcePath, headers); 1128 | } 1129 | 1130 | # User Posts timeline by User ID 1131 | # 1132 | # + id - The ID of the User to lookup 1133 | # + headers - Headers to be sent with the request 1134 | # + queries - Queries to be sent with the request 1135 | # + return - The request has succeeded 1136 | resource isolated function get users/[UserId id]/tweets(map headers = {}, *UsersIdTweetsQueries queries) returns Get2UsersIdTweetsResponse|error { 1137 | string resourcePath = string `/users/${getEncodedUri(id)}/tweets`; 1138 | map queryParamEncoding = {"exclude": {style: FORM, explode: false}, "tweet.fields": {style: FORM, explode: false}, "expansions": {style: FORM, explode: false}, "media.fields": {style: FORM, explode: false}, "poll.fields": {style: FORM, explode: false}, "user.fields": {style: FORM, explode: false}, "place.fields": {style: FORM, explode: false}}; 1139 | resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); 1140 | return self.clientEp->get(resourcePath, headers); 1141 | } 1142 | 1143 | # Unfollow User 1144 | # 1145 | # + sourceUserId - The ID of the authenticated source User that is requesting to unfollow the target User 1146 | # + targetUserId - The ID of the User that the source User is requesting to unfollow 1147 | # + headers - Headers to be sent with the request 1148 | # + return - The request has succeeded 1149 | resource isolated function delete users/[UserIdMatchesAuthenticatedUser sourceUserId]/following/[UserId targetUserId](map headers = {}) returns UsersFollowingDeleteResponse|error { 1150 | string resourcePath = string `/users/${getEncodedUri(sourceUserId)}/following/${getEncodedUri(targetUserId)}`; 1151 | return self.clientEp->delete(resourcePath, headers = headers); 1152 | } 1153 | 1154 | # Unmute User by User ID 1155 | # 1156 | # + sourceUserId - The ID of the authenticated source User that is requesting to unmute the target User 1157 | # + targetUserId - The ID of the User that the source User is requesting to unmute 1158 | # + headers - Headers to be sent with the request 1159 | # + return - The request has succeeded 1160 | resource isolated function delete users/[UserIdMatchesAuthenticatedUser sourceUserId]/muting/[UserId targetUserId](map headers = {}) returns MuteUserMutationResponse|error { 1161 | string resourcePath = string `/users/${getEncodedUri(sourceUserId)}/muting/${getEncodedUri(targetUserId)}`; 1162 | return self.clientEp->delete(resourcePath, headers = headers); 1163 | } 1164 | } 1165 | --------------------------------------------------------------------------------