diff --git a/.gitignore b/.gitignore index 2163ca8..bab2381 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,6 @@ build # Ignore Docker env file docker.env + +# Ignore Roup +.roup diff --git a/README.md b/README.md index e6485ea..b91d99a 100644 --- a/README.md +++ b/README.md @@ -8,21 +8,181 @@ ## Overview -[//]: # (TODO: Add overview mentioning the purpose of the module, supported REST API versions, and other high-level details.) +[HubSpot](https://www.hubspot.com/) is an AI-powered customer relationship management (CRM) platform. + +The `ballerinax/hubspot.automation.actions` offers APIs to connect and interact with the [Automation Actions](https://developers.hubspot.com/docs/reference/api/automation/custom-workflow-actions) endpoints, specifically based on the [HubSpot REST API](https://developers.hubspot.com/docs/reference/api/automation/custom-workflow-actions) ## Setup guide -[//]: # (TODO: Add detailed steps to obtain credentials and configure the module.) +To use the HubSpot Automation action API connector in Ballerina, you must have a HubSpot developer account. If you don't have an account, create one using the following steps. + +### Step 1: Create a HubSpot Developer Account + +Visit the [HubSpot portal](https://developers.hubspot.com/get-started) and create a Developer Account. + +### Step 2: Create a HubSpot Developer Test Account + +Visit [developer test account page](https://developers.hubspot.com/beta-docs/getting-started/account-types#developer-test-accounts) and create a HubSpot developer test account. + +### Step 3: Create a HubSpot Public App + +In your developer account, navigate to the "Apps" section. + +Click on "Create App" and provide the necessary details, including the app name and description. +### Step 4: Initiate the OAuth Flow + +Move to the Auth tab in the created app and set the permissions there. + +Under the OAuth tab you can find the following details, +* `client_id`: Your app's Client ID. +* `redirect_uri`: The URL users will be redirected to after granting access. +* `scope`: A space-separated list of scopes your app is requesting. + +![Auth Tab example](https://raw.githubusercontent.com/ballerina-platform/module-ballerinax-hubspot.automation.actions/docs/setup/resources/auth.png) + +### Step 5: Add the redirect URL + +Add your redirect url under the redirect urls. + +![add the redirect url](https://raw.githubusercontent.com/ballerina-platform/module-ballerinax-hubspot.automation.actions/docs/setup/resources/redirect_url.png) + +### Step 6: Add the Required Scopes + +For Automation Actions, the required scopes are; +* 'automation' + + +![Required Scopes](https://raw.githubusercontent.com/ballerina-platform/module-ballerinax-hubspot.automation.actions/docs/setup/resources/scopes.png) + +Save the app + +![Save the app](https://raw.githubusercontent.com/ballerina-platform/module-ballerinax-hubspot.automation.actions/docs/setup/resources/save.png) + +### Step 7: Obtain the authorization code + +Copy the App installation url and paste it in the web browser. + +![Redirect URL ](https://raw.githubusercontent.com/ballerina-platform/module-ballerinax-hubspot.automation.actions/docs/setup/resources/redirect.png) + + It wll prompt you to install the App and then select your developer test account. + +After selecting the developer test account, you will receive a authorization code displayed in the browser. + +![Obtain the authorization code](https://raw.githubusercontent.com/ballerina-platform/module-ballerinax-hubspot.automation.actions/docs/setup/resources/authorization_code.png) + +### Step 8: Obtain the access token + +Place your ``, `` and `` in the following command and execute it in the terminal + +'curl --request POST \ + --url https://api.hubapi.com/oauth/v1/token \ + --header 'content-type: application/x-www-form-urlencoded' \ + --data 'grant_type=authorization_code&code=&redirect_uri=http://localhost:9090&client_id=&client_secret=' + +In order to receive a token we need to run a listener at the provided redirect uri. + +If the command executes successfully , you will receive the access token from the response. + +### Step 9: Obtaining the developer API key + +Follow the instructions at (https://developers.hubspot.com/docs/api/developer-tools-overview#developer-api-keys) to obtain the developer API key. ## Quickstart -[//]: # (TODO: Add a quickstart guide to demonstrate a basic functionality of the module, including sample code snippets.) +To begin using the `HubSpot Automation API` connector in your Ballerina application, you'll need to follow these steps: + +### Step 1: Import the connector + +First, import the `ballerinax/hubspot.automation.actions` package into your Ballerina project. + +```ballerina +import ballerinax/hubspot.automation.actions; +``` + +### Step 2: Instantiate a new connector + +Create a `actions:ConnectionConfig` object with your domain and developer API token, and initialize the connector. + +```ballerina +actions:ConnectionConfig config = { + auth: { + hapikey: "" , + private\-app\-legacy: "" + } +}; +final actions:Client hubspotAutomation = check new (config); +``` + +### Step 3: Invoke the connector operation + +Utilize the connector's operations to manage extensions and functions. + +#### Create an extension + +```ballerina + +actions:FieldTypeDefinition typeDefinition = { + referencedObjectType: "OWNER", + externalOptions: false, + externalOptionsReferenceType: "", + name: "optionsInput", + 'type: "enumeration", + fieldType: "select", + optionsUrl: "https://webhook.site/94d09471-6f4c-4a7f-bae2-c9a585dd41e0", + options: [] +}; + +actions:InputFieldDefinition inputFieldDefinition = { + isRequired: true, + automationFieldType: "", + typeDefinition: typeDefinition, + supportedValueTypes: ["STATIC_VALUE"] +}; + + + +actions:PublicActionFunction publicActionFunction = { + functionSource: "exports.main = (event, callback) => {\r\n callback({\r\n outputFields: {\r\n myOutput: \"example output value\"\r\n }\r\n });\r\n}", + functionType: "POST_ACTION_EXECUTION" +}; + +actions:PublicActionDefinitionEgg testingPublicActionDefinitionEgg = { + inputFields: [inputFieldDefinition], + actionUrl: "https://webhook.site/94d09471-6f4c-4a7f-bae2-c9a585dd41e0", + published: false, + objectTypes: ["CONTACT"], + objectRequestOptions: { properties: ["email"] }, + functions: [publicActionFunction], + labels: { + "en": { + "inputFieldLabels": { + "staticInput": "Static Input", + "objectInput": "Object Property Input", + "optionsInput": "External Options Input" + }, + "actionName": "My Extension", + "actionDescription": "My Extension Description", + "appDisplayName": "My App Display Name", + "actionCardContent": "My Action Card Content" + } + } +}; + +actions: PublicActionDefinition response = check hubspotAutomation->/automation/v4/actions/[appId].post(testingPublicActionDefinitionEgg); +``` + +#### List definitions + +```ballerina +actions : CollectionResponsePublicActionDefinitionForwardPaging response = check hubspotAutomation->/automation/v4/actions/[appId]; +``` ## Examples -The `HubSpot Automation Action` connector provides practical examples illustrating usage in various scenarios. Explore these [examples](https://github.com/module-ballerinax-hubspot.automation.actions/tree/main/examples/), covering the following use cases: +The `HubSpot Automation API` connector provides practical examples illustrating usage in various scenarios. Explore these [examples](https://github.com/ballerina-platform/module-ballerinax-hubspot.automation.actions/tree/main/examples/), covering the following use cases: -[//]: # (TODO: Add examples) +1. [Extension CRUD](https://github.com/ballerina-platform/module-ballerinax-hubspot.automation.actions/tree/main/examples/)- Perform CRUD operations on Extensions +2. [Call complete callback APIs](https://github.com/ballerina-platform/module-ballerinax-hubspot.automation.actions/tree/main/examples/) - Complete callbacks using the HubSpot API ## Build from the source diff --git a/ballerina/Ballerina.toml b/ballerina/Ballerina.toml index 17a4df5..7265c29 100644 --- a/ballerina/Ballerina.toml +++ b/ballerina/Ballerina.toml @@ -5,12 +5,12 @@ name = "hubspot.automation.actions" version = "1.0.0" license = ["Apache-2.0"] authors = ["Ballerina"] -keywords = [] -# icon = "icon.png" # TODO: update icon.png +keywords = ["HubSpot","automation","actions","ballerina","integration", "connector"] +icon = "icon.png" repository = "https://github.com/ballerina-platform/module-ballerinax-hubspot.automation.actions" [build-options] observabilityIncluded = true -[platform.java21] +[platform.java17] graalvmCompatible = true diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml new file mode 100644 index 0000000..a0f2601 --- /dev/null +++ b/ballerina/Dependencies.toml @@ -0,0 +1,324 @@ +# AUTO-GENERATED FILE. DO NOT MODIFY. + +# This file is auto-generated by Ballerina for managing dependency versions. +# It should not be modified by hand. + +[ballerina] +dependencies-toml-version = "2" +distribution-version = "2201.10.3" + +[[package]] +org = "ballerina" +name = "auth" +version = "2.12.0" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.array"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "log"} +] + +[[package]] +org = "ballerina" +name = "cache" +version = "3.8.0" +dependencies = [ + {org = "ballerina", name = "constraint"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "task"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "constraint" +version = "1.5.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "crypto" +version = "2.7.2" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "file" +version = "1.10.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "os"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "http" +version = "2.12.4" +dependencies = [ + {org = "ballerina", name = "auth"}, + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "constraint"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "file"}, + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "jwt"}, + {org = "ballerina", name = "lang.array"}, + {org = "ballerina", name = "lang.decimal"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "lang.regexp"}, + {org = "ballerina", name = "lang.runtime"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "lang.value"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "mime"}, + {org = "ballerina", name = "oauth2"}, + {org = "ballerina", name = "observe"}, + {org = "ballerina", name = "time"}, + {org = "ballerina", name = "url"} +] +modules = [ + {org = "ballerina", packageName = "http", moduleName = "http"}, + {org = "ballerina", packageName = "http", moduleName = "http.httpscerr"} +] + +[[package]] +org = "ballerina" +name = "io" +version = "1.6.3" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"} +] + +[[package]] +org = "ballerina" +name = "jballerina.java" +version = "0.0.0" + +[[package]] +org = "ballerina" +name = "jwt" +version = "2.13.0" +dependencies = [ + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "lang.__internal" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.object"} +] + +[[package]] +org = "ballerina" +name = "lang.array" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.__internal"} +] + +[[package]] +org = "ballerina" +name = "lang.decimal" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.error" +version = "0.0.0" +scope = "testOnly" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.int" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.__internal"}, + {org = "ballerina", name = "lang.object"} +] + +[[package]] +org = "ballerina" +name = "lang.object" +version = "0.0.0" + +[[package]] +org = "ballerina" +name = "lang.regexp" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.runtime" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.string" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.regexp"} +] + +[[package]] +org = "ballerina" +name = "lang.value" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "log" +version = "2.10.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"}, + {org = "ballerina", name = "observe"} +] + +[[package]] +org = "ballerina" +name = "mime" +version = "2.10.1" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "log"} +] + +[[package]] +org = "ballerina" +name = "oauth2" +version = "2.12.0" +dependencies = [ + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "time"}, + {org = "ballerina", name = "url"} +] + +[[package]] +org = "ballerina" +name = "observe" +version = "1.3.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "os" +version = "1.8.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "task" +version = "2.5.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "test" +version = "0.0.0" +scope = "testOnly" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.array"}, + {org = "ballerina", name = "lang.error"} +] +modules = [ + {org = "ballerina", packageName = "test", moduleName = "test"} +] + +[[package]] +org = "ballerina" +name = "time" +version = "2.4.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "url" +version = "2.4.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] +modules = [ + {org = "ballerina", packageName = "url", moduleName = "url"} +] + +[[package]] +org = "ballerinai" +name = "observe" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "observe"} +] +modules = [ + {org = "ballerinai", packageName = "observe", moduleName = "observe"} +] + +[[package]] +org = "ballerinax" +name = "hubspot.automation.actions" +version = "1.0.0" +dependencies = [ + {org = "ballerina", name = "http"}, + {org = "ballerina", name = "test"}, + {org = "ballerina", name = "url"}, + {org = "ballerinai", name = "observe"} +] +modules = [ + {org = "ballerinax", packageName = "hubspot.automation.actions", moduleName = "hubspot.automation.actions"} +] + diff --git a/ballerina/Module.md b/ballerina/Module.md index 5a4f740..aea29fd 100644 --- a/ballerina/Module.md +++ b/ballerina/Module.md @@ -1,17 +1,183 @@ ## Overview -[//]: # (TODO: Add overview mentioning the purpose of the module, supported REST API versions, and other high-level details.) +[HubSpot ](https://www.hubspot.com/) is an AI-powered customer relationship management (CRM) platform. + +The `ballerinax/hubspot.automation.actions` offers APIs to connect and interact with the [Automation Actions](https://developers.hubspot.com/docs/reference/api/automation/custom-workflow-actions) endpoints, specifically based on the [HubSpot REST API](https://developers.hubspot.com/docs/reference/api/automation/custom-workflow-actions) ## Setup guide -[//]: # (TODO: Add detailed steps to obtain credentials and configure the module.) +To use the HubSpot Automation action API connector in Ballerina, you must have a HubSpot developer account. If you don't have an account, create one using the following steps. + +### Step 1: Create a HubSpot Developer Account + +Visit the [HubSpot portal](https://developers.hubspot.com/get-started) and create a Developer Account. + +### Step 2: Create a HubSpot Developer Test Account + +Visit [developer test account page](https://developers.hubspot.com/beta-docs/getting-started/account-types#developer-test-accounts) and create a HubSpot developer test account. +### Step 3: Create a HubSpot Public App + +In your developer account, navigate to the "Apps" section. + +Click on "Create App" and provide the necessary details, including the app name and description. + + +### Step 4: Initiate the OAuth Flow + +Move to the auth tab in the created app and set the permissions there. + +Under the OAuth tab you can find the following details, + +* `client_id`: Your app's Client ID. +* `redirect_uri`: The URL users will be redirected to after granting access. +* `scope`: A space-separated list of scopes your app is requesting. + +![Auth Tab example](https://raw.githubusercontent.com/ballerina-platform/module-ballerinax-hubspot.automation.actions/docs/setup/resources/auth.png) + +### Step 5: Add the redirect URL + +Add your redirect url under the redirect urls. + +![add the redirect url](https://raw.githubusercontent.com/ballerina-platform/module-ballerinax-hubspot.automation.actions/docs/setup/resources/redirect_url.png) + +### Step 6: Add the Required Scopes + +For Automation Actions, the required scopes are; +* 'automation' + +![Required Scopes](https://raw.githubusercontent.com/ballerina-platform/module-ballerinax-hubspot.automation.actions/docs/setup/resources/scopes.png) + +Save the app + +![Save the app](https://raw.githubusercontent.com/ballerina-platform/module-ballerinax-hubspot.automation.actions/docs/setup/resources/save.png) + +### Step 7: Obtain the authorization code + +Copy the App installation url and paste it in the web browser. + +![Redirect URL ](https://raw.githubusercontent.com/ballerina-platform/module-ballerinax-hubspot.automation.actions/docs/setup/resources/redirect.png) + + It wll prompt you to install the App and then select your deveper test account. + +After selecting the developer test account, you will receive a authorization code displayed in the browser. + +![Obtain the authorization code](https://raw.githubusercontent.com/ballerina-platform/module-ballerinax-hubspot.automation.actions/docs/setup/resources/authorization_code.png) + +### Step 8: Obtain the access token + +Place your **authorization_code**, **client_id** and **client_secret** in the folowing comand and execute it in the terminal + +'curl --request POST \ + --url https://api.hubapi.com/oauth/v1/token \ + --header 'content-type: application/x-www-form-urlencoded' \ + --data 'grant_type=authorization_code&code=&redirect_uri=http://localhost:9090&client_id=&client_secret=' + +In order to receive a token we need to run a listener at the provided redirect uri. + +If the command executes successfully , you will receive the access token from the response. + +### Step 9: Obtaining the developer API key + +Follow the instructions at (https://developers.hubspot.com/docs/api/developer-tools-overview#developer-api-keys) to obtain the developer API key. ## Quickstart -[//]: # (TODO: Add a quickstart guide to demonstrate a basic functionality of the module, including sample code snippets.) +To begin using the `HubSpot Automation API` connector in your Ballerina application, you'll need to follow these steps: + +### Step 1: Import the connector + +First, import the `ballerinax/hubspot.automation.actions` package into your Ballerina project. + +```ballerina +import ballerinax/hubspot.automation.actions; +``` + +### Step 2: Instantiate a new connector + +Create a `actions:ConnectionConfig` object with your domain and developer API token, and initialize the connector. + +```ballerina +actions:ConnectionConfig config = { + auth:{ + hapikey:"" , + private\-app\-legacy: "" +} +}; +actions:Client hubspotAutomation = check new (config); +``` + +### Step 3: Invoke the connector operation + +Utilize the connector's operations to manage extensions and functions. + +#### Create an extension + +```ballerina + +actions:FieldTypeDefinition typeDefinition = { + referencedObjectType: "OWNER", + externalOptions: false, + externalOptionsReferenceType: "", + name: "optionsInput", + 'type: "enumeration", + fieldType: "select", + optionsUrl: "https://webhook.site/94d09471-6f4c-4a7f-bae2-c9a585dd41e0", + options: [] +}; + +actions:InputFieldDefinition inputFieldDefinition = { + isRequired: true, + automationFieldType: "", + typeDefinition: typeDefinition, + supportedValueTypes: ["STATIC_VALUE"] +}; + + + +actions:PublicActionFunction publicActionFunction = { + functionSource: "exports.main = (event, callback) => {\r\n callback({\r\n outputFields: {\r\n myOutput: \"example output value\"\r\n }\r\n });\r\n}", + functionType: "POST_ACTION_EXECUTION" +}; + +actions:PublicActionDefinitionEgg testingPublicActionDefinitionEgg = { + inputFields: [inputFieldDefinition], + actionUrl: "https://webhook.site/94d09471-6f4c-4a7f-bae2-c9a585dd41e0", + published: false, + objectTypes: ["CONTACT"], + objectRequestOptions: { properties: ["email"] }, + functions: [publicActionFunction], + labels: { + "en": { + "inputFieldLabels": { + "staticInput": "Static Input", + "objectInput": "Object Property Input", + "optionsInput": "External Options Input" + }, + "actionName": "My Extension", + "actionDescription": "My Extension Description", + "appDisplayName": "My App Display Name", + "actionCardContent": "My Action Card Content" + } + } +}; + + + + +actions: PublicActionDefinition response = check hubspotAutomation->/automation/v4/actions/[appId].post(testingPublicActionDefinitionEgg); +``` + +#### List definitions + +```ballerina + +actions : CollectionResponsePublicActionDefinitionForwardPaging response = check hubspotAutomation->/automation/v4/actions/[appId]; + +``` ## Examples -The `HubSpot Automation Action` connector provides practical examples illustrating usage in various scenarios. Explore these [examples](https://github.com/module-ballerinax-hubspot.automation.actions/tree/main/examples/), covering the following use cases: +The `HubSpot Automation API` connector provides practical examples illustrating usage in various scenarios. Explore these [examples](https://github.com/ballerina-platform/module-ballerinax-hubspot.automation.actions/tree/main/examples/), covering the following use cases: -[//]: # (TODO: Add examples) +1. [Extension CRUD](https://github.com/ballerina-platform/module-ballerinax-hubspot.automation.actions/tree/main/examples/)- Perform CRUD operations on Extensions +2. [Call complete callback APIs](https://github.com/ballerina-platform/module-ballerinax-hubspot.automation.actions/tree/main/examples/) - Complete callbacks using the HubSpot API diff --git a/ballerina/Package.md b/ballerina/Package.md index 5a4f740..4a83c8c 100644 --- a/ballerina/Package.md +++ b/ballerina/Package.md @@ -1,17 +1,184 @@ ## Overview -[//]: # (TODO: Add overview mentioning the purpose of the module, supported REST API versions, and other high-level details.) +[HubSpot](https://www.hubspot.com/) is an AI-powered customer relationship management (CRM) platform. + +The `ballerinax/hubspot.automation.actions` offers APIs to connect and interact with the [Automation Actions](https://developers.hubspot.com/docs/reference/api/automation/custom-workflow-actions) endpoints, specifically based on the [HubSpot REST API](https://developers.hubspot.com/docs/reference/api/automation/custom-workflow-actions) ## Setup guide -[//]: # (TODO: Add detailed steps to obtain credentials and configure the module.) +To use the HubSpot Automation action API connector in Ballerina, you must have a HubSpot developer account. If you don't have an account, create one using the following steps. + +### Step 1: Create a HubSpot Developer Account + +Visit the [HubSpot portal](https://developers.hubspot.com/get-started) and create a Developer Account. + +### Step 2: Create a HubSpot Developer Test Account + +Visit [developer test account page](https://developers.hubspot.com/beta-docs/getting-started/account-types#developer-test-accounts) and create a HubSpot developer test account. + +### Step 3: Create a HubSpot Public App + +In your developer account, navigate to the "Apps" section. + +Click on "Create App" and provide the necessary details, including the app name and description. + +### Step 4: Initiate the OAuth Flow + +Move to the Auth tab in the created app and set the permissions there. + +Under the OAuth tab you can find the following details, + +* `client_id`: Your app's Client ID. +* `redirect_uri`: The URL users will be redirected to after granting access. +* `scope`: A space-separated list of scopes your app is requesting. + +![Auth Tab example](https://raw.githubusercontent.com/ballerina-platform/module-ballerinax-hubspot.automation.actions/docs/setup/resources/auth.png) + +### Step 5: Add the redirect URL + +Add your redirect url under the redirect urls. + +![add the redirect url](https://raw.githubusercontent.com/ballerina-platform/module-ballerinax-hubspot.automation.actions/docs/setup/resources/redirect_url.png) + +### Step 6: Add the Required Scopes + +For Automation Actions, the required scopes are; +* 'automation' + +![Required Scopes](https://raw.githubusercontent.com/ballerina-platform/module-ballerinax-hubspot.automation.actions/docs/setup/resources/scopes.png) + +Save the app + +![Save the app](https://raw.githubusercontent.com/ballerina-platform/module-ballerinax-hubspot.automation.actions/docs/setup/resources/save.png) + +### Step 7: Obtain the authorization code + +Copy the App installation url and paste it in the web browser. + +![Redirect URL ](https://raw.githubusercontent.com/ballerina-platform/module-ballerinax-hubspot.automation.actions/docs/setup/resources/redirect.png) + + It wll prompt you to install the App and then select your developer test account. + +After selecting the developer test account, you will receive a authorization code displayed in the browser. + +![Obtain the authorization code](https://raw.githubusercontent.com/ballerina-platform/module-ballerinax-hubspot.automation.actions/docs/setup/resources/authorization_code.png) + +### Step 8: Obtain the access token + +Place your **authorization_code**, **client_id** and **client_secret** in the folowing comand and execute it in the terminal + +'curl --request POST \ + --url https://api.hubapi.com/oauth/v1/token \ + --header 'content-type: application/x-www-form-urlencoded' \ + --data 'grant_type=authorization_code&code=&redirect_uri=http://localhost:9090&client_id=&client_secret=' + +In order to receive a token we need to run a listener at the provided redirect uri. + +If the command executes successfully , you will receive the access token from the response. + +### Step 9: Obtaining the developer API key + +Follow the instructions at (https://developers.hubspot.com/docs/api/developer-tools-overview#developer-api-keys) to obtain the developer API key. ## Quickstart -[//]: # (TODO: Add a quickstart guide to demonstrate a basic functionality of the module, including sample code snippets.) +To begin using the `HubSpot Automation API` connector in your Ballerina application, you'll need to follow these steps: + +### Step 1: Import the connector + +First, import the `ballerinax/hubspot.automation.actions` package into your Ballerina project. + +```ballerina +import ballerinax/hubspot.automation.actions; +``` + +### Step 2: Instantiate a new connector + +Create a `actions:ConnectionConfig` object with your domain and developer API token, and initialize the connector. + +```ballerina +actions:ConnectionConfig config = { + auth:{ + hapikey:"" , + private\-app\-legacy: "" +} +}; +actions:Client hubspotAutomation = check new (config); +``` + +### Step 3: Invoke the connector operation + +Utilize the connector's operations to manage extensions and functions. + +#### Create an extension + +```ballerina + +actions:FieldTypeDefinition typeDefinition = { + referencedObjectType: "OWNER", + externalOptions: false, + externalOptionsReferenceType: "", + name: "optionsInput", + 'type: "enumeration", + fieldType: "select", + optionsUrl: "https://webhook.site/94d09471-6f4c-4a7f-bae2-c9a585dd41e0", + options: [] +}; + +actions:InputFieldDefinition inputFieldDefinition = { + isRequired: true, + automationFieldType: "", + typeDefinition: typeDefinition, + supportedValueTypes: ["STATIC_VALUE"] +}; + + + +actions:PublicActionFunction publicActionFunction = { + functionSource: "exports.main = (event, callback) => {\r\n callback({\r\n outputFields: {\r\n myOutput: \"example output value\"\r\n }\r\n });\r\n}", + functionType: "POST_ACTION_EXECUTION" +}; + +actions:PublicActionDefinitionEgg testingPublicActionDefinitionEgg = { + inputFields: [inputFieldDefinition], + actionUrl: "https://webhook.site/94d09471-6f4c-4a7f-bae2-c9a585dd41e0", + published: false, + objectTypes: ["CONTACT"], + objectRequestOptions: { properties: ["email"] }, + functions: [publicActionFunction], + labels: { + "en": { + "inputFieldLabels": { + "staticInput": "Static Input", + "objectInput": "Object Property Input", + "optionsInput": "External Options Input" + }, + "actionName": "My Extension", + "actionDescription": "My Extension Description", + "appDisplayName": "My App Display Name", + "actionCardContent": "My Action Card Content" + } + } +}; + + + + +actions: PublicActionDefinition response = check hubspotAutomation->/automation/v4/actions/[appId].post(testingPublicActionDefinitionEgg); +``` + +#### List definitions + +```ballerina + +actions : CollectionResponsePublicActionDefinitionForwardPaging response = check hubspotAutomation->/automation/v4/actions/[appId]; + +``` ## Examples -The `HubSpot Automation Action` connector provides practical examples illustrating usage in various scenarios. Explore these [examples](https://github.com/module-ballerinax-hubspot.automation.actions/tree/main/examples/), covering the following use cases: -[//]: # (TODO: Add examples) +The `HubSpot Automation API` connector provides practical examples illustrating usage in various scenarios. Explore these [examples](https://github.com/ballerina-platform/module-ballerinax-hubspot.automation.actions/tree/main/examples/), covering the following use cases: + +1. [Extension CRUD](https://github.com/ballerina-platform/module-ballerinax-hubspot.automation.actions/tree/main/examples/)- Perform CRUD operations on Extensions +2. [Call complete callback APIs](https://github.com/ballerina-platform/module-ballerinax-hubspot.automation.actions/tree/main/examples/) - Complete callbacks using the HubSpot API diff --git a/ballerina/client.bal b/ballerina/client.bal index 66cdc3f..492dd5d 100644 --- a/ballerina/client.bal +++ b/ballerina/client.bal @@ -1,4 +1,4 @@ -// Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). +// Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). // // WSO2 LLC. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except @@ -13,3 +13,291 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. + +import ballerina/http; + +public isolated client class Client { + final http:Client clientEp; + final readonly & ApiKeysConfig? apiKeyConfig; + # Gets invoked to initialize the `connector`. + # + # + config - The configurations to be used when initializing the `connector` + # + serviceUrl - URL of the target service + # + return - An error if connector initialization failed + public isolated function init(ConnectionConfig config, string serviceUrl = "https://api.hubapi.com/automation/v4/actions") returns error? { + http:ClientConfiguration httpClientConfig = {httpVersion: config.httpVersion, timeout: config.timeout, forwarded: config.forwarded, poolConfig: config.poolConfig, compression: config.compression, circuitBreaker: config.circuitBreaker, retryConfig: config.retryConfig, validation: config.validation}; + do { + if config.http1Settings is ClientHttp1Settings { + ClientHttp1Settings settings = check config.http1Settings.ensureType(ClientHttp1Settings); + httpClientConfig.http1Settings = {...settings}; + } + if config.http2Settings is http:ClientHttp2Settings { + httpClientConfig.http2Settings = check config.http2Settings.ensureType(http:ClientHttp2Settings); + } + if config.cache is http:CacheConfig { + httpClientConfig.cache = check config.cache.ensureType(http:CacheConfig); + } + if config.responseLimits is http:ResponseLimitConfigs { + httpClientConfig.responseLimits = check config.responseLimits.ensureType(http:ResponseLimitConfigs); + } + if config.secureSocket is http:ClientSecureSocket { + httpClientConfig.secureSocket = check config.secureSocket.ensureType(http:ClientSecureSocket); + } + if config.proxy is http:ProxyConfig { + httpClientConfig.proxy = check config.proxy.ensureType(http:ProxyConfig); + } + } + if config.auth is ApiKeysConfig { + self.apiKeyConfig = (config.auth).cloneReadOnly(); + } else { + httpClientConfig.auth = config.auth; + self.apiKeyConfig = (); + } + http:Client httpEp = check new (serviceUrl, httpClientConfig); + self.clientEp = httpEp; + return; + } + + # Archive an extension definition + # + # + headers - Headers to be sent with the request + # + return - No content + resource isolated function delete [int:Signed32 appId]/[string definitionId](map headers = {}) returns http:Response|error { + string resourcePath = string `/${getEncodedUri(appId)}/${getEncodedUri(definitionId)}`; + map queryParam = {}; + if self.apiKeyConfig is ApiKeysConfig { + queryParam["hapikey"] = self.apiKeyConfig?.hapikey; + } + resourcePath = resourcePath + check getPathForQueryParam(queryParam); + return self.clientEp->delete(resourcePath, headers = headers); + } + + # Delete a function for a definition + # + # + headers - Headers to be sent with the request + # + return - No content + resource isolated function delete [int:Signed32 appId]/[string definitionId]/functions/["PRE_ACTION_EXECUTION"|"PRE_FETCH_OPTIONS"|"POST_FETCH_OPTIONS"|"POST_ACTION_EXECUTION" functionType](map headers = {}) returns http:Response|error { + string resourcePath = string `/${getEncodedUri(appId)}/${getEncodedUri(definitionId)}/functions/${getEncodedUri(functionType)}`; + map queryParam = {}; + if self.apiKeyConfig is ApiKeysConfig { + queryParam["hapikey"] = self.apiKeyConfig?.hapikey; + } + resourcePath = resourcePath + check getPathForQueryParam(queryParam); + return self.clientEp->delete(resourcePath, headers = headers); + } + + # Archive a function for a definition + # + # + headers - Headers to be sent with the request + # + return - No content + resource isolated function delete [int:Signed32 appId]/[string definitionId]/functions/["PRE_ACTION_EXECUTION"|"PRE_FETCH_OPTIONS"|"POST_FETCH_OPTIONS"|"POST_ACTION_EXECUTION" functionType]/[string functionId](map headers = {}) returns http:Response|error { + string resourcePath = string `/${getEncodedUri(appId)}/${getEncodedUri(definitionId)}/functions/${getEncodedUri(functionType)}/${getEncodedUri(functionId)}`; + map queryParam = {}; + if self.apiKeyConfig is ApiKeysConfig { + queryParam["hapikey"] = self.apiKeyConfig?.hapikey; + } + resourcePath = resourcePath + check getPathForQueryParam(queryParam); + return self.clientEp->delete(resourcePath, headers = headers); + } + + # Get paged extension definitions + # + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request + # + return - successful operation + resource isolated function get [int:Signed32 appId](map headers = {}, *GetAppid_getpageQueries queries) returns CollectionResponsePublicActionDefinitionForwardPaging|error { + string resourcePath = string `/${getEncodedUri(appId)}`; + map queryParam = {...queries}; + if self.apiKeyConfig is ApiKeysConfig { + queryParam["hapikey"] = self.apiKeyConfig?.hapikey; + } + resourcePath = resourcePath + check getPathForQueryParam(queryParam); + return self.clientEp->get(resourcePath, headers); + } + + # Get extension definition by Id + # + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request + # + return - successful operation + resource isolated function get [int:Signed32 appId]/[string definitionId](map headers = {}, *GetAppidDefinitionid_getbyidQueries queries) returns PublicActionDefinition|error { + string resourcePath = string `/${getEncodedUri(appId)}/${getEncodedUri(definitionId)}`; + map queryParam = {...queries}; + if self.apiKeyConfig is ApiKeysConfig { + queryParam["hapikey"] = self.apiKeyConfig?.hapikey; + } + resourcePath = resourcePath + check getPathForQueryParam(queryParam); + return self.clientEp->get(resourcePath, headers); + } + + # Get all functions for a given definition + # + # + headers - Headers to be sent with the request + # + return - successful operation + resource isolated function get [int:Signed32 appId]/[string definitionId]/functions(map headers = {}) returns CollectionResponsePublicActionFunctionIdentifierNoPaging|error { + string resourcePath = string `/${getEncodedUri(appId)}/${getEncodedUri(definitionId)}/functions`; + map queryParam = {}; + if self.apiKeyConfig is ApiKeysConfig { + queryParam["hapikey"] = self.apiKeyConfig?.hapikey; + } + resourcePath = resourcePath + check getPathForQueryParam(queryParam); + return self.clientEp->get(resourcePath, headers); + } + + # Get all functions by a type for a given definition + # + # + headers - Headers to be sent with the request + # + return - successful operation + resource isolated function get [int:Signed32 appId]/[string definitionId]/functions/["PRE_ACTION_EXECUTION"|"PRE_FETCH_OPTIONS"|"POST_FETCH_OPTIONS"|"POST_ACTION_EXECUTION" functionType](map headers = {}) returns PublicActionFunction|error { + string resourcePath = string `/${getEncodedUri(appId)}/${getEncodedUri(definitionId)}/functions/${getEncodedUri(functionType)}`; + map queryParam = {}; + if self.apiKeyConfig is ApiKeysConfig { + queryParam["hapikey"] = self.apiKeyConfig?.hapikey; + } + resourcePath = resourcePath + check getPathForQueryParam(queryParam); + return self.clientEp->get(resourcePath, headers); + } + + # Get a function for a given definition + # + # + headers - Headers to be sent with the request + # + return - successful operation + resource isolated function get [int:Signed32 appId]/[string definitionId]/functions/["PRE_ACTION_EXECUTION"|"PRE_FETCH_OPTIONS"|"POST_FETCH_OPTIONS"|"POST_ACTION_EXECUTION" functionType]/[string functionId](map headers = {}) returns PublicActionFunction|error { + string resourcePath = string `/${getEncodedUri(appId)}/${getEncodedUri(definitionId)}/functions/${getEncodedUri(functionType)}/${getEncodedUri(functionId)}`; + map queryParam = {}; + if self.apiKeyConfig is ApiKeysConfig { + queryParam["hapikey"] = self.apiKeyConfig?.hapikey; + } + resourcePath = resourcePath + check getPathForQueryParam(queryParam); + return self.clientEp->get(resourcePath, headers); + } + + # Get all revisions for a given definition + # + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request + # + return - successful operation + resource isolated function get [int:Signed32 appId]/[string definitionId]/revisions(map headers = {}, *GetAppidDefinitionidRevisions_getpageQueries queries) returns CollectionResponsePublicActionRevisionForwardPaging|error { + string resourcePath = string `/${getEncodedUri(appId)}/${getEncodedUri(definitionId)}/revisions`; + map queryParam = {...queries}; + if self.apiKeyConfig is ApiKeysConfig { + queryParam["hapikey"] = self.apiKeyConfig?.hapikey; + } + resourcePath = resourcePath + check getPathForQueryParam(queryParam); + return self.clientEp->get(resourcePath, headers); + } + + # Gets a revision for a given definition by revision id + # + # + headers - Headers to be sent with the request + # + return - successful operation + resource isolated function get [int:Signed32 appId]/[string definitionId]/revisions/[string revisionId](map headers = {}) returns PublicActionRevision|error { + string resourcePath = string `/${getEncodedUri(appId)}/${getEncodedUri(definitionId)}/revisions/${getEncodedUri(revisionId)}`; + map queryParam = {}; + if self.apiKeyConfig is ApiKeysConfig { + queryParam["hapikey"] = self.apiKeyConfig?.hapikey; + } + resourcePath = resourcePath + check getPathForQueryParam(queryParam); + return self.clientEp->get(resourcePath, headers); + } + + # Patch an existing extension definition + # + # + headers - Headers to be sent with the request + # + return - successful operation + resource isolated function patch [int:Signed32 appId]/[string definitionId](PublicActionDefinitionPatch payload, map headers = {}) returns PublicActionDefinition|error { + string resourcePath = string `/${getEncodedUri(appId)}/${getEncodedUri(definitionId)}`; + map queryParam = {}; + if self.apiKeyConfig is ApiKeysConfig { + queryParam["hapikey"] = self.apiKeyConfig?.hapikey; + } + resourcePath = resourcePath + check getPathForQueryParam(queryParam); + http:Request request = new; + json jsonBody = payload.toJson(); + request.setPayload(jsonBody, "application/json"); + return self.clientEp->patch(resourcePath, request, headers); + } + + # Create a new extension definition + # + # + headers - Headers to be sent with the request + # + return - successful operation + resource isolated function post [int:Signed32 appId](PublicActionDefinitionEgg payload, map headers = {}) returns PublicActionDefinition|error { + string resourcePath = string `/${getEncodedUri(appId)}`; + map queryParam = {}; + if self.apiKeyConfig is ApiKeysConfig { + queryParam["hapikey"] = self.apiKeyConfig?.hapikey; + } + resourcePath = resourcePath + check getPathForQueryParam(queryParam); + http:Request request = new; + json jsonBody = payload.toJson(); + request.setPayload(jsonBody, "application/json"); + return self.clientEp->post(resourcePath, request, headers); + } + + # Completes a single callback + # + # + headers - Headers to be sent with the request + # + return - No content + resource isolated function post callbacks/[string callbackId]/complete(CallbackCompletionRequest payload, map headers = {}) returns http:Response|error { + string resourcePath = string `/callbacks/${getEncodedUri(callbackId)}/complete`; + map headerValues = {...headers}; + if self.apiKeyConfig is ApiKeysConfig { + headerValues["private-app-legacy"] = self.apiKeyConfig?.private\-app\-legacy; + } + map httpHeaders = getMapForHeaders(headerValues); + http:Request request = new; + json jsonBody = payload.toJson(); + request.setPayload(jsonBody, "application/json"); + return self.clientEp->post(resourcePath, request, httpHeaders); + } + + # Completes a batch of callbacks + # + # + headers - Headers to be sent with the request + # + return - No content + resource isolated function post callbacks/complete(BatchInputCallbackCompletionBatchRequest payload, map headers = {}) returns http:Response|error { + string resourcePath = string `/callbacks/complete`; + map headerValues = {...headers}; + if self.apiKeyConfig is ApiKeysConfig { + headerValues["private-app-legacy"] = self.apiKeyConfig?.private\-app\-legacy; + } + map httpHeaders = getMapForHeaders(headerValues); + http:Request request = new; + json jsonBody = payload.toJson(); + request.setPayload(jsonBody, "application/json"); + return self.clientEp->post(resourcePath, request, httpHeaders); + } + + # Insert a function for a definition + # + # + headers - Headers to be sent with the request + # + return - successful operation + resource isolated function put [int:Signed32 appId]/[string definitionId]/functions/["PRE_ACTION_EXECUTION"|"PRE_FETCH_OPTIONS"|"POST_FETCH_OPTIONS"|"POST_ACTION_EXECUTION" functionType](string payload, map headers = {}) returns PublicActionFunctionIdentifier|error { + string resourcePath = string `/${getEncodedUri(appId)}/${getEncodedUri(definitionId)}/functions/${getEncodedUri(functionType)}`; + map queryParam = {}; + if self.apiKeyConfig is ApiKeysConfig { + queryParam["hapikey"] = self.apiKeyConfig?.hapikey; + } + resourcePath = resourcePath + check getPathForQueryParam(queryParam); + http:Request request = new; + request.setPayload(payload, "text/plain"); + return self.clientEp->put(resourcePath, request, headers); + } + + # Insert a function for a definition + # + # + headers - Headers to be sent with the request + # + return - successful operation + resource isolated function put [int:Signed32 appId]/[string definitionId]/functions/["PRE_ACTION_EXECUTION"|"PRE_FETCH_OPTIONS"|"POST_FETCH_OPTIONS"|"POST_ACTION_EXECUTION" functionType]/[string functionId](string payload, map headers = {}) returns PublicActionFunctionIdentifier|error { + string resourcePath = string `/${getEncodedUri(appId)}/${getEncodedUri(definitionId)}/functions/${getEncodedUri(functionType)}/${getEncodedUri(functionId)}`; + map queryParam = {}; + if self.apiKeyConfig is ApiKeysConfig { + queryParam["hapikey"] = self.apiKeyConfig?.hapikey; + } + resourcePath = resourcePath + check getPathForQueryParam(queryParam); + http:Request request = new; + request.setPayload(payload, "text/plain"); + return self.clientEp->put(resourcePath, request, headers); + } +} diff --git a/ballerina/icon.png b/ballerina/icon.png new file mode 100644 index 0000000..1f93076 Binary files /dev/null and b/ballerina/icon.png differ diff --git a/ballerina/tests/mockService.bal b/ballerina/tests/mockService.bal new file mode 100644 index 0000000..a00b9cb --- /dev/null +++ b/ballerina/tests/mockService.bal @@ -0,0 +1,43 @@ +// Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/http; +import ballerina/test; + +service /mock on new http:Listener(8080) { + + resource function post callbacks/complete(http:Caller caller, http:Request req) returns error? { + + // Mock response + http:Response res = new; + res.statusCode = 204; + check caller->respond(res); + }; +} + +@test:BeforeSuite +function startMockService() { + // Code to start the mock service + // This is automatically handled by Ballerina when the service is defined + +} + +@test:AfterSuite +function stopMockService() { + // Code to stop the mock service + // This is automatically handled by Ballerina when the service is defined + +} diff --git a/ballerina/tests/test.bal b/ballerina/tests/test.bal new file mode 100644 index 0000000..073044e --- /dev/null +++ b/ballerina/tests/test.bal @@ -0,0 +1,293 @@ +// Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/http; +import ballerina/test; + +configurable boolean isLiveServer =false; +configurable boolean isOauth = ?; +configurable string oauthKey = ?; +configurable string apiKey = ?; + +int:Signed32 appId = 5712614; + +// API Key Config +ConnectionConfig apikeyConfig = { + auth: { + hapikey: apiKey, + private\-app\-legacy: "" + } +}; + +string serviceUrl = isLiveServer ? "https://api.hubapi.com/automation/v4/actions" : "http://localhost:8080/mock"; + + +// Client initialization +final Client hubspotAutomation = check new Client(apikeyConfig, serviceUrl); + +// Sample Extension Definition +string createdExtensionId = ""; + +FieldTypeDefinition typeDefinition = { + referencedObjectType: "OWNER", + externalOptions: false, + externalOptionsReferenceType: "", + name: "optionsInput", + 'type: "enumeration", + fieldType: "select", + optionsUrl: "https://webhook.site/94d09471-6f4c-4a7f-bae2-c9a585dd41e0", + options: [] +}; + +InputFieldDefinition inputFieldDefinition = { + isRequired: true, + automationFieldType: "", + typeDefinition: typeDefinition, + supportedValueTypes: ["STATIC_VALUE"] +}; + +PublicActionFunction publicActionFunction = { + functionSource: "exports.main = (event, callback) => {\r\n callback({\r\n outputFields: {\r\n myOutput: \"example output value\"\r\n }\r\n });\r\n}", + functionType: "POST_ACTION_EXECUTION" +}; + +PublicActionDefinitionEgg testingPublicActionDefinitionEgg = { + inputFields: [inputFieldDefinition], + actionUrl: "https://webhook.site/94d09471-6f4c-4a7f-bae2-c9a585dd41e0", + published: false, + objectTypes: ["CONTACT"], + objectRequestOptions: {properties: ["email"]}, + functions: [publicActionFunction], + labels: { + "en": { + inputFieldLabels: { + "staticInput": "Static Input", + "objectInput": "Object Property Input", + "optionsInput": "External Options Input" + }, + actionName: "My Extension", + actionDescription: "My Extension Description", + appDisplayName: "My App Display Name", + actionCardContent: "My Action Card Content" + } + } +}; + +# create Extension definition +# +# + return - error? if an error occurs, null otherwise +# +@test:Config { + groups: ["apikey"], + enable: isLiveServer} +function testPost() returns error? { + PublicActionDefinition response = check hubspotAutomation->/[appId].post(testingPublicActionDefinitionEgg); + + // Assert creation success and set the global ID + test:assertTrue(response?.id is string, "Extension creation failed"); + + createdExtensionId = response.id; +} + +# Insert a function for a definition +# +# + return - error? if an error occurs, null otherwise +# +@test:Config { + groups: ["apikey"], + dependsOn: [testPost], + enable: isLiveServer +} +function testPostFunction() returns error? { + PublicActionFunctionIdentifier response = check hubspotAutomation->/[appId]/[createdExtensionId]/functions/["POST_FETCH_OPTIONS"].put("exports.main = (event, callback) => {\r\n callback({\r\n \"options\": [{\r\n \"label\": \"Big Widget\",\r\n \"description\": \"Big Widget\",\r\n \"value\": \"10\"\r\n },\r\n {\r\n \"label\": \"Small Widget\",\r\n \"description\": \"Small Widget\",\r\n \"value\": \"1\"\r\n }\r\n ]\r\n });\r\n}"); + + // assert function creation success + test:assertTrue(response?.functionType === "POST_FETCH_OPTIONS", "Function creation failed"); +} + +@test:Config { + dependsOn: [testPost], + groups: ["apikey"], + enable: isLiveServer +} +function testGetDefinitionById() returns error? { + PublicActionDefinition response = check hubspotAutomation->/[appId]/[createdExtensionId]; + + // Validate the retrieved extension's ID + test:assertTrue(response?.id === createdExtensionId, "Extension retrieval failed"); + test:assertEquals(response?.functions[0]?.functionType, "POST_ACTION_EXECUTION", "Function type mismatch"); + test:assertEquals(response?.functions[1]?.functionType, "POST_FETCH_OPTIONS", "Function type mismatch"); + test:assertEquals(response?.actionUrl, "https://webhook.site/94d09471-6f4c-4a7f-bae2-c9a585dd41e0", "Action URL mismatch"); + test:assertEquals(response?.published, false, "Published status mismatch"); + test:assertEquals(response?.labels["en"]?.appDisplayName, "My App Display Name", "App display name mismatch"); + test:assertEquals(response?.labels["en"]?.actionDescription, "My Extension Description", "Action description mismatch"); + test:assertEquals(response?.labels["en"]?.inputFieldLabels["optionsInput"], "External Options Input", "Input field label mismatch"); + test:assertEquals(response?.labels["en"]?.actionName, "My Extension", "Action name mismatch"); + test:assertEquals(response?.labels["en"]?.actionCardContent, "My Action Card Content", "Action card content mismatch"); + test:assertEquals(response?.inputFields[0]?.isRequired, true, "Input field required status mismatch"); + test:assertEquals(response?.inputFields[0]?.typeDefinition?.referencedObjectType, "OWNER", "Referenced object type mismatch"); + test:assertEquals(response?.inputFields[0]?.typeDefinition?.name, "optionsInput", "Input field name mismatch"); + test:assertEquals(response?.inputFields[0]?.typeDefinition?.optionsUrl, "https://webhook.site/94d09471-6f4c-4a7f-bae2-c9a585dd41e0", "Options URL mismatch"); + test:assertEquals(response?.revisionId, "1", "Revision ID mismatch"); + test:assertEquals(response?.objectTypes[0], "0-1", "Object type mismatch"); + +} + +# Get all functions for a given definition +# +# + return - error? if an error occurs, null otherwise + +@test:Config { + dependsOn: [testPostFunction], + groups: ["apikey"], + enable: isLiveServer + +} +function testGetAllFunctions() returns error? { + CollectionResponsePublicActionFunctionIdentifierNoPaging response = check hubspotAutomation->/[appId]/[createdExtensionId]/functions; + + //validate the response + test:assertTrue(response?.results.length() > 0, "No functions found for the extension"); + +} + +# Get paged extension definitions +# +# + return - error? if an error occurs, null otherwise +# +@test:Config {groups: ["apikey"], + enable: isLiveServer +} +function testGetPagedExtensionDefinitions() returns error? { + CollectionResponsePublicActionDefinitionForwardPaging response = check hubspotAutomation->/[appId]; + + //validate the response + test:assertTrue(response?.results.length() > 0, "No extension definitions found"); +} + +# Get all revisions for a given definition +# +# + return - error? if an error occurs, null otherwise +# +@test:Config { + dependsOn: [testPost], + groups: ["apikey"], + enable: isLiveServer + +} +function testGetAllRevisions() returns error? { + CollectionResponsePublicActionRevisionForwardPaging response = check hubspotAutomation->/[appId]/[createdExtensionId]/revisions; + + // assert response + test:assertTrue(response?.results.length() > 0, "No revisions found for the extension"); +} + +# Get a revision for a given definition by revision ID +# +# + return - error? if an error occurs, null otherwise +# +@test:Config { + dependsOn: [testPost], + groups: ["apikey"], + enable: isLiveServer + +} +function testGetRevision() returns error? { + PublicActionRevision response = check hubspotAutomation->/[appId]/[createdExtensionId]/revisions/["1"]; + + // assert response + test:assertTrue(response?.revisionId === "1", "Revision retrieval failed"); + test:assertEquals(response?.definition?.actionUrl, "https://webhook.site/94d09471-6f4c-4a7f-bae2-c9a585dd41e0", "Action URL mismatch"); + test:assertEquals(response?.definition?.published, false, "Published status mismatch"); + test:assertEquals(response?.definition?.inputFields[0]?.typeDefinition?.referencedObjectType, "OWNER", "Referenced object type mismatch"); + test:assertEquals(response?.definition?.inputFields[0]?.typeDefinition?.fieldType, "select", "Field type mismatch"); + test:assertEquals(response?.definition?.inputFields[0]?.typeDefinition?.optionsUrl, "https://webhook.site/94d09471-6f4c-4a7f-bae2-c9a585dd41e0", "Options URL mismatch"); + test:assertEquals(response?.definition?.revisionId, "1", "Revision ID mismatch"); + +} + +# Archive an extension definition +# +# + return - error? if an error occurs, null otherwise +# +@test:Config { + dependsOn: [testPost, testDeleteFunction], + groups: ["apikey"], + enable: isLiveServer + +} +function testDelete() returns error? { + + http:Response response = check hubspotAutomation->/[appId]/[createdExtensionId].delete(); + + // assert response + test:assertTrue(response.statusCode == 204, "Extension deletion failed"); + +} + +# Delete a function for a definition +# +# + return - error? if an error occurs, null otherwise +# +@test:Config { + dependsOn: [testPost], + groups: ["apikey"], + enable: isLiveServer + +} +function testDeleteFunction() returns error? { + PublicActionFunction response = check hubspotAutomation->/[appId]/[createdExtensionId]/functions/["POST_ACTION_EXECUTION"]; + // validate response + test:assertTrue(response?.functionType === "POST_ACTION_EXECUTION", "Function deletion failed"); + test:assertTrue(response?.functionSource== "exports.main = (event, callback) => {\r\n callback({\r\n outputFields: {\r\n myOutput: \"example output value\"\r\n }\r\n });\r\n}", "Function deletion failed"); + +} + +# Completes a batch of callbacks +# +# + return - error? if an error occurs, null otherwise +# +@test:Config { + groups: ["oauth"], + enable: !isLiveServer + + } +function testRespondBatch() returns error? { + + // BearerTokenConfig + ConnectionConfig oauthConfig = { + auth: { + token: oauthKey + } + }; + + final Client hubspotAutomationOauth = check new Client(oauthConfig, serviceUrl); + + BatchInputCallbackCompletionBatchRequest batchCallbackCompletionRequest = { + inputs: [ + { + callbackId: "1", + outputFields: { + "exampleField": "exampleValue" + } + } + ] + }; + http:Response response = check hubspotAutomationOauth->/callbacks/complete.post(batchCallbackCompletionRequest); + + // assert response + test:assertTrue(response.statusCode == 204, "Batch completion failed"); +} diff --git a/ballerina/types.bal b/ballerina/types.bal new file mode 100644 index 0000000..708c682 --- /dev/null +++ b/ballerina/types.bal @@ -0,0 +1,277 @@ +// Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/http; + +public type PublicActionFunction record { + string functionSource; + "PRE_ACTION_EXECUTION"|"PRE_FETCH_OPTIONS"|"POST_FETCH_OPTIONS"|"POST_ACTION_EXECUTION" functionType; + string id?; +}; + +public type PublicActionDefinition record { + PublicActionFunctionIdentifier[] functions; + string actionUrl; + boolean published; + record {|PublicActionLabels...;|} labels; + InputFieldDefinition[] inputFields; + OutputFieldDefinition[] outputFields?; + string revisionId; + int archivedAt?; + (PublicSingleFieldDependency|PublicConditionalSingleFieldDependency)[] inputFieldDependencies?; + PublicExecutionTranslationRule[] executionRules?; + string id; + string[] objectTypes; + PublicObjectRequestOptions objectRequestOptions?; +}; + +public type PublicConditionalSingleFieldDependency record { + "CONDITIONAL_SINGLE_FIELD" dependencyType; + string controllingFieldName; + string controllingFieldValue; + string[] dependentFieldNames; +}; + +public type PublicObjectRequestOptions record { + string[] properties; +}; + +public type PublicSingleFieldDependency record { + "SINGLE_FIELD" dependencyType; + string controllingFieldName; + string[] dependentFieldNames; +}; + +public type PublicActionLabels record { + record {|string...;|} inputFieldDescriptions?; + string appDisplayName?; + record {|string...;|} outputFieldLabels?; + record {|record {|string...;|}...;|} inputFieldOptionLabels?; + string actionDescription?; + record {|string...;|} executionRules?; + record {|string...;|} inputFieldLabels?; + string actionName; + string actionCardContent?; +}; + +public type ForwardPaging record { + NextPage next?; +}; + +# Represents the Queries record for the operation: get-/{appId}/{definitionId}/revisions_getPage +public type GetAppidDefinitionidRevisions_getpageQueries record { + # The maximum number of results to display per page. + int:Signed32 'limit?; + # The paging cursor token of the last successfully read resource will be returned as the `paging.next.after` JSON property of a paged response containing more results. + string after?; +}; + +public type FieldTypeDefinition record { + string helpText?; + "CONTACT"|"COMPANY"|"DEAL"|"ENGAGEMENT"|"TICKET"|"OWNER"|"PRODUCT"|"LINE_ITEM"|"BET_DELIVERABLE_SERVICE"|"CONTENT"|"CONVERSATION"|"BET_ALERT"|"PORTAL"|"QUOTE"|"FORM_SUBMISSION_INBOUNDDB"|"QUOTA"|"UNSUBSCRIBE"|"COMMUNICATION"|"FEEDBACK_SUBMISSION"|"ATTRIBUTION"|"SALESFORCE_SYNC_ERROR"|"RESTORABLE_CRM_OBJECT"|"HUB"|"LANDING_PAGE"|"PRODUCT_OR_FOLDER"|"TASK"|"FORM"|"MARKETING_EMAIL"|"AD_ACCOUNT"|"AD_CAMPAIGN"|"AD_GROUP"|"AD"|"KEYWORD"|"CAMPAIGN"|"SOCIAL_CHANNEL"|"SOCIAL_POST"|"SITE_PAGE"|"BLOG_POST"|"IMPORT"|"EXPORT"|"CTA"|"TASK_TEMPLATE"|"AUTOMATION_PLATFORM_FLOW"|"OBJECT_LIST"|"NOTE"|"MEETING_EVENT"|"CALL"|"EMAIL"|"PUBLISHING_TASK"|"CONVERSATION_SESSION"|"CONTACT_CREATE_ATTRIBUTION"|"INVOICE"|"MARKETING_EVENT"|"CONVERSATION_INBOX"|"CHATFLOW"|"MEDIA_BRIDGE"|"SEQUENCE"|"SEQUENCE_STEP"|"FORECAST"|"SNIPPET"|"TEMPLATE"|"DEAL_CREATE_ATTRIBUTION"|"QUOTE_TEMPLATE"|"QUOTE_MODULE"|"QUOTE_MODULE_FIELD"|"QUOTE_FIELD"|"SEQUENCE_ENROLLMENT"|"SUBSCRIPTION"|"ACCEPTANCE_TEST"|"SOCIAL_BROADCAST"|"DEAL_SPLIT"|"DEAL_REGISTRATION"|"GOAL_TARGET"|"GOAL_TARGET_GROUP"|"PORTAL_OBJECT_SYNC_MESSAGE"|"FILE_MANAGER_FILE"|"FILE_MANAGER_FOLDER"|"SEQUENCE_STEP_ENROLLMENT"|"APPROVAL"|"APPROVAL_STEP"|"CTA_VARIANT"|"SALES_DOCUMENT"|"DISCOUNT"|"FEE"|"TAX"|"MARKETING_CALENDAR"|"PERMISSIONS_TESTING"|"PRIVACY_SCANNER_COOKIE"|"DATA_SYNC_STATE"|"WEB_INTERACTIVE"|"PLAYBOOK"|"FOLDER"|"PLAYBOOK_QUESTION"|"PLAYBOOK_SUBMISSION"|"PLAYBOOK_SUBMISSION_ANSWER"|"COMMERCE_PAYMENT"|"GSC_PROPERTY"|"SOX_PROTECTED_DUMMY_TYPE"|"BLOG_LISTING_PAGE"|"QUARANTINED_SUBMISSION"|"PAYMENT_SCHEDULE"|"PAYMENT_SCHEDULE_INSTALLMENT"|"MARKETING_CAMPAIGN_UTM"|"DISCOUNT_TEMPLATE"|"DISCOUNT_CODE"|"FEEDBACK_SURVEY"|"CMS_URL"|"SALES_TASK"|"SALES_WORKLOAD"|"USER"|"POSTAL_MAIL"|"SCHEMAS_BACKEND_TEST"|"PAYMENT_LINK"|"SUBMISSION_TAG"|"CAMPAIGN_STEP"|"SCHEDULING_PAGE"|"SOX_PROTECTED_TEST_TYPE"|"ORDER"|"MARKETING_SMS"|"PARTNER_ACCOUNT"|"CAMPAIGN_TEMPLATE"|"CAMPAIGN_TEMPLATE_STEP"|"PLAYLIST"|"CLIP"|"CAMPAIGN_BUDGET_ITEM"|"CAMPAIGN_SPEND_ITEM"|"MIC"|"CONTENT_AUDIT"|"CONTENT_AUDIT_PAGE"|"PLAYLIST_FOLDER"|"LEAD"|"ABANDONED_CART"|"EXTERNAL_WEB_URL"|"VIEW"|"VIEW_BLOCK"|"ROSTER"|"CART"|"AUTOMATION_PLATFORM_FLOW_ACTION"|"SOCIAL_PROFILE"|"PARTNER_CLIENT"|"ROSTER_MEMBER"|"MARKETING_EVENT_ATTENDANCE"|"ALL_PAGES"|"AI_FORECAST"|"CRM_PIPELINES_DUMMY_TYPE"|"KNOWLEDGE_ARTICLE"|"PROPERTY_INFO"|"DATA_PRIVACY_CONSENT"|"GOAL_TEMPLATE"|"SCORE_CONFIGURATION"|"AUDIENCE"|"PARTNER_CLIENT_REVENUE"|"AUTOMATION_JOURNEY"|"UNKNOWN" referencedObjectType?; + string name; + Option[] options; + string description?; + string externalOptionsReferenceType?; + string label?; + "string"|"number"|"bool"|"datetime"|"enumeration"|"date"|"phone_number"|"currency_number"|"json"|"object_coordinates" 'type; + "booleancheckbox"|"checkbox"|"date"|"file"|"number"|"phonenumber"|"radio"|"select"|"text"|"textarea"|"calculation_equation"|"calculation_rollup"|"calculation_score"|"calculation_read_time"|"unknown"|"html" fieldType?; + string optionsUrl?; + boolean externalOptions; +}; + +public type CollectionResponsePublicActionFunctionIdentifierNoPaging record { + PublicActionFunctionIdentifier[] results; +}; + +public type InputFieldDefinition record { + boolean isRequired; + string automationFieldType?; + FieldTypeDefinition typeDefinition; + ("STATIC_VALUE"|"OBJECT_PROPERTY"|"FIELD_DATA"|"FETCHED_OBJECT_PROPERTY"|"ENROLLMENT_EVENT_PROPERTY")[] supportedValueTypes?; +}; + +# OAuth2 Refresh Token Grant Configs +public type OAuth2RefreshTokenGrantConfig record {| + *http:OAuth2RefreshTokenGrantConfig; + # Refresh URL + string refreshUrl = "https://api.hubapi.com/oauth/v1/token"; +|}; + +public type PublicExecutionTranslationRule record { + string labelName; + record {|record {}...;|} conditions; +}; + +public type PublicActionFunctionIdentifier record { + "PRE_ACTION_EXECUTION"|"PRE_FETCH_OPTIONS"|"POST_FETCH_OPTIONS"|"POST_ACTION_EXECUTION" functionType; + string id?; +}; + +# Provides a set of configurations for controlling the behaviours when communicating with a remote HTTP endpoint. +@display {label: "Connection Config"} +public type ConnectionConfig record {| + # Provides Auth configurations needed when communicating with a remote HTTP endpoint. + http:BearerTokenConfig|OAuth2RefreshTokenGrantConfig|ApiKeysConfig auth; + # The HTTP version understood by the client + http:HttpVersion httpVersion = http:HTTP_2_0; + # Configurations related to HTTP/1.x protocol + ClientHttp1Settings http1Settings?; + # Configurations related to HTTP/2 protocol + http:ClientHttp2Settings http2Settings?; + # The maximum time to wait (in seconds) for a response before closing the connection + decimal timeout = 60; + # The choice of setting `forwarded`/`x-forwarded` header + string forwarded = "disable"; + # Configurations associated with request pooling + http:PoolConfiguration poolConfig?; + # HTTP caching related configurations + http:CacheConfig cache?; + # Specifies the way of handling compression (`accept-encoding`) header + http:Compression compression = http:COMPRESSION_AUTO; + # Configurations associated with the behaviour of the Circuit Breaker + http:CircuitBreakerConfig circuitBreaker?; + # Configurations associated with retrying + http:RetryConfig retryConfig?; + # Configurations associated with inbound response size limits + http:ResponseLimitConfigs responseLimits?; + # SSL/TLS-related options + http:ClientSecureSocket secureSocket?; + # Proxy server related options + http:ProxyConfig proxy?; + # Enables the inbound payload validation functionality which provided by the constraint package. Enabled by default + boolean validation = true; +|}; + +public type PublicActionRevision record { + string revisionId; + string createdAt; + PublicActionDefinition definition; + string id; +}; + +public type CallbackCompletionRequest record { + record {|string...;|} outputFields; +}; + +public type CollectionResponsePublicActionDefinitionForwardPaging record { + ForwardPaging paging?; + PublicActionDefinition[] results; +}; + +public type CollectionResponsePublicActionRevisionForwardPaging record { + ForwardPaging paging?; + PublicActionRevision[] results; +}; + +# Proxy server configurations to be used with the HTTP client endpoint. +public type ProxyConfig record {| + # Host name of the proxy server + string host = ""; + # Proxy server port + int port = 0; + # Proxy server username + string userName = ""; + # Proxy server password + @display {label: "", kind: "password"} + string password = ""; +|}; + +public type CallbackCompletionBatchRequest record { + record {|string...;|} outputFields; + string callbackId; +}; + +# Represents the Queries record for the operation: get-/{appId}_getPage +public type GetAppid_getpageQueries record { + # Whether to return only results that have been archived. + boolean archived = false; + # The maximum number of results to display per page. + int:Signed32 'limit?; + # The paging cursor token of the last successfully read resource will be returned as the `paging.next.after` JSON property of a paged response containing more results. + string after?; +}; + +public type PublicActionDefinitionEgg record { + InputFieldDefinition[] inputFields; + OutputFieldDefinition[] outputFields?; + int archivedAt?; + PublicActionFunction[] functions; + string actionUrl; + (PublicSingleFieldDependency|PublicConditionalSingleFieldDependency)[] inputFieldDependencies?; + boolean published; + PublicExecutionTranslationRule[] executionRules?; + string[] objectTypes; + PublicObjectRequestOptions objectRequestOptions?; + record {|PublicActionLabels...;|} labels; +}; + +# Provides settings related to HTTP/1.x protocol. +public type ClientHttp1Settings record {| + # Specifies whether to reuse a connection for multiple requests + http:KeepAlive keepAlive = http:KEEPALIVE_AUTO; + # The chunking behaviour of the request + http:Chunking chunking = http:CHUNKING_AUTO; + # Proxy server related options + ProxyConfig proxy?; +|}; + +public type PublicActionDefinitionPatch record { + InputFieldDefinition[] inputFields?; + OutputFieldDefinition[] outputFields?; + string actionUrl?; + (PublicSingleFieldDependency|PublicConditionalSingleFieldDependency)[] inputFieldDependencies?; + boolean published?; + PublicExecutionTranslationRule[] executionRules?; + string[] objectTypes?; + PublicObjectRequestOptions objectRequestOptions?; + record {|PublicActionLabels...;|} labels?; +}; + +public type BatchInputCallbackCompletionBatchRequest record { + CallbackCompletionBatchRequest[] inputs; +}; + +public type Option record { + boolean hidden; + int:Signed32 displayOrder; + decimal doubleData; + string description; + boolean readOnly; + string label; + string value; +}; + +public type OutputFieldDefinition record { + FieldTypeDefinition typeDefinition; +}; + +# Represents the Queries record for the operation: get-/{appId}/{definitionId}_getById +public type GetAppidDefinitionid_getbyidQueries record { + # Whether to return only results that have been archived. + boolean archived = false; +}; + +public type NextPage record { + string link?; + string after; +}; + +# Provides API key configurations needed when communicating with a remote HTTP endpoint. +public type ApiKeysConfig record {| + string hapikey; + string private\-app\-legacy; +|}; diff --git a/ballerina/utils.bal b/ballerina/utils.bal new file mode 100644 index 0000000..b2aff1f --- /dev/null +++ b/ballerina/utils.bal @@ -0,0 +1,231 @@ +// Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/url; + +type SimpleBasicType string|boolean|int|float|decimal; + +# Represents encoding mechanism details. +type Encoding record { + # Defines how multiple values are delimited + string style = FORM; + # Specifies whether arrays and objects should generate as separate fields + boolean explode = true; + # Specifies the custom content type + string contentType?; + # Specifies the custom headers + map headers?; +}; + +enum EncodingStyle { + DEEPOBJECT, FORM, SPACEDELIMITED, PIPEDELIMITED +} + +final Encoding & readonly defaultEncoding = {}; + +# Serialize the record according to the deepObject style. +# +# + parent - Parent record name +# + anyRecord - Record to be serialized +# + return - Serialized record as a string +isolated function getDeepObjectStyleRequest(string parent, record {} anyRecord) returns string { + string[] recordArray = []; + foreach [string, anydata] [key, value] in anyRecord.entries() { + if value is SimpleBasicType { + recordArray.push(parent + "[" + key + "]" + "=" + getEncodedUri(value.toString())); + } else if value is SimpleBasicType[] { + recordArray.push(getSerializedArray(parent + "[" + key + "]" + "[]", value, DEEPOBJECT, true)); + } else if value is record {} { + string nextParent = parent + "[" + key + "]"; + recordArray.push(getDeepObjectStyleRequest(nextParent, value)); + } else if value is record {}[] { + string nextParent = parent + "[" + key + "]"; + recordArray.push(getSerializedRecordArray(nextParent, value, DEEPOBJECT)); + } + recordArray.push("&"); + } + _ = recordArray.pop(); + return string:'join("", ...recordArray); +} + +# Serialize the record according to the form style. +# +# + parent - Parent record name +# + anyRecord - Record to be serialized +# + explode - Specifies whether arrays and objects should generate separate parameters +# + return - Serialized record as a string +isolated function getFormStyleRequest(string parent, record {} anyRecord, boolean explode = true) returns string { + string[] recordArray = []; + if explode { + foreach [string, anydata] [key, value] in anyRecord.entries() { + if value is SimpleBasicType { + recordArray.push(key, "=", getEncodedUri(value.toString())); + } else if value is SimpleBasicType[] { + recordArray.push(getSerializedArray(key, value, explode = explode)); + } else if value is record {} { + recordArray.push(getFormStyleRequest(parent, value, explode)); + } + recordArray.push("&"); + } + _ = recordArray.pop(); + } else { + foreach [string, anydata] [key, value] in anyRecord.entries() { + if value is SimpleBasicType { + recordArray.push(key, ",", getEncodedUri(value.toString())); + } else if value is SimpleBasicType[] { + recordArray.push(getSerializedArray(key, value, explode = false)); + } else if value is record {} { + recordArray.push(getFormStyleRequest(parent, value, explode)); + } + recordArray.push(","); + } + _ = recordArray.pop(); + } + return string:'join("", ...recordArray); +} + +# Serialize arrays. +# +# + arrayName - Name of the field with arrays +# + anyArray - Array to be serialized +# + style - Defines how multiple values are delimited +# + explode - Specifies whether arrays and objects should generate separate parameters +# + return - Serialized array as a string +isolated function getSerializedArray(string arrayName, anydata[] anyArray, string style = "form", boolean explode = true) returns string { + string key = arrayName; + string[] arrayValues = []; + if anyArray.length() > 0 { + if style == FORM && !explode { + arrayValues.push(key, "="); + foreach anydata i in anyArray { + arrayValues.push(getEncodedUri(i.toString()), ","); + } + } else if style == SPACEDELIMITED && !explode { + arrayValues.push(key, "="); + foreach anydata i in anyArray { + arrayValues.push(getEncodedUri(i.toString()), "%20"); + } + } else if style == PIPEDELIMITED && !explode { + arrayValues.push(key, "="); + foreach anydata i in anyArray { + arrayValues.push(getEncodedUri(i.toString()), "|"); + } + } else if style == DEEPOBJECT { + foreach anydata i in anyArray { + arrayValues.push(key, "[]", "=", getEncodedUri(i.toString()), "&"); + } + } else { + foreach anydata i in anyArray { + arrayValues.push(key, "=", getEncodedUri(i.toString()), "&"); + } + } + _ = arrayValues.pop(); + } + return string:'join("", ...arrayValues); +} + +# Serialize the array of records according to the form style. +# +# + parent - Parent record name +# + value - Array of records to be serialized +# + style - Defines how multiple values are delimited +# + explode - Specifies whether arrays and objects should generate separate parameters +# + return - Serialized record as a string +isolated function getSerializedRecordArray(string parent, record {}[] value, string style = FORM, boolean explode = true) returns string { + string[] serializedArray = []; + if style == DEEPOBJECT { + int arayIndex = 0; + foreach var recordItem in value { + serializedArray.push(getDeepObjectStyleRequest(parent + "[" + arayIndex.toString() + "]", recordItem), "&"); + arayIndex = arayIndex + 1; + } + } else { + if !explode { + serializedArray.push(parent, "="); + } + foreach var recordItem in value { + serializedArray.push(getFormStyleRequest(parent, recordItem, explode), ","); + } + } + _ = serializedArray.pop(); + return string:'join("", ...serializedArray); +} + +# Get Encoded URI for a given value. +# +# + value - Value to be encoded +# + return - Encoded string +isolated function getEncodedUri(anydata value) returns string { + string|error encoded = url:encode(value.toString(), "UTF8"); + if encoded is string { + return encoded; + } else { + return value.toString(); + } +} + +# Generate query path with query parameter. +# +# + queryParam - Query parameter map +# + encodingMap - Details on serialization mechanism +# + return - Returns generated Path or error at failure of client initialization +isolated function getPathForQueryParam(map queryParam, map encodingMap = {}) returns string|error { + string[] param = []; + if queryParam.length() > 0 { + param.push("?"); + foreach var [key, value] in queryParam.entries() { + if value is () { + _ = queryParam.remove(key); + continue; + } + Encoding encodingData = encodingMap.hasKey(key) ? encodingMap.get(key) : defaultEncoding; + if value is SimpleBasicType { + param.push(key, "=", getEncodedUri(value.toString())); + } else if value is SimpleBasicType[] { + param.push(getSerializedArray(key, value, encodingData.style, encodingData.explode)); + } else if value is record {} { + if encodingData.style == DEEPOBJECT { + param.push(getDeepObjectStyleRequest(key, value)); + } else { + param.push(getFormStyleRequest(key, value, encodingData.explode)); + } + } else { + param.push(key, "=", value.toString()); + } + param.push("&"); + } + _ = param.pop(); + } + string restOfPath = string:'join("", ...param); + return restOfPath; +} + +# Generate header map for given header values. +# +# + headerParam - Headers map +# + return - Returns generated map or error at failure of client initialization +isolated function getMapForHeaders(map headerParam) returns map { + map headerMap = {}; + foreach var [key, value] in headerParam.entries() { + if value is SimpleBasicType[] { + headerMap[key] = from SimpleBasicType data in value + select data.toString(); + } else { + headerMap[key] = value.toString(); + } + } + return headerMap; +} diff --git a/build-config/resources/Ballerina.toml b/build-config/resources/Ballerina.toml index 29420a6..3dd3b9e 100644 --- a/build-config/resources/Ballerina.toml +++ b/build-config/resources/Ballerina.toml @@ -5,12 +5,12 @@ name = "hubspot.automation.actions" version = "@toml.version@" license = ["Apache-2.0"] authors = ["Ballerina"] -keywords = [] # TODO: Add keywords -# icon = "icon.png" # TODO: Add icon +keywords = ["HubSpot","automation","actions","ballerina","integration", "connector"] +icon = "icon.png" repository = "https://github.com/ballerina-platform/module-ballerinax-hubspot.automation.actions" [build-options] observabilityIncluded = true -[platform.java21] +[platform.java17] graalvmCompatible = true diff --git a/build-config/resources/icon.png b/build-config/resources/icon.png new file mode 100644 index 0000000..1f93076 Binary files /dev/null and b/build-config/resources/icon.png differ diff --git a/docs/license.txt b/docs/license.txt index 921a7a1..81552b8 100644 --- a/docs/license.txt +++ b/docs/license.txt @@ -1,7 +1,4 @@ -// AUTO-GENERATED FILE. DO NOT MODIFY. -// This file is auto-generated by the Ballerina OpenAPI tool. - -// Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). +// Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). // // WSO2 LLC. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except @@ -15,4 +12,4 @@ // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations -// under the License. +// under the License. \ No newline at end of file diff --git a/docs/setup/resources/auth.png b/docs/setup/resources/auth.png new file mode 100644 index 0000000..016295d Binary files /dev/null and b/docs/setup/resources/auth.png differ diff --git a/docs/setup/resources/authorization_code.png b/docs/setup/resources/authorization_code.png new file mode 100644 index 0000000..558bb73 Binary files /dev/null and b/docs/setup/resources/authorization_code.png differ diff --git a/docs/setup/resources/redirect.png b/docs/setup/resources/redirect.png new file mode 100644 index 0000000..a082d27 Binary files /dev/null and b/docs/setup/resources/redirect.png differ diff --git a/docs/setup/resources/redirect_url.png b/docs/setup/resources/redirect_url.png new file mode 100644 index 0000000..99648ce Binary files /dev/null and b/docs/setup/resources/redirect_url.png differ diff --git a/docs/setup/resources/save.png b/docs/setup/resources/save.png new file mode 100644 index 0000000..f4c8cc5 Binary files /dev/null and b/docs/setup/resources/save.png differ diff --git a/docs/setup/resources/scope_doc.png b/docs/setup/resources/scope_doc.png new file mode 100644 index 0000000..f9f616f Binary files /dev/null and b/docs/setup/resources/scope_doc.png differ diff --git a/docs/setup/resources/scopes.png b/docs/setup/resources/scopes.png new file mode 100644 index 0000000..025083f Binary files /dev/null and b/docs/setup/resources/scopes.png differ diff --git a/docs/spec/openapi.json b/docs/spec/openapi.json new file mode 100644 index 0000000..0ff4a31 --- /dev/null +++ b/docs/spec/openapi.json @@ -0,0 +1,1600 @@ +{ + "openapi" : "3.0.1", + "info" : { + "title" : "Automation Actions V4", + "version" : "v4", + "x-hubspot-product-tier-requirements" : { + "marketing" : "PROFESSIONAL", + "sales" : "PROFESSIONAL", + "service" : "PROFESSIONAL" + }, + "x-hubspot-documentation-banner" : "NONE", + "x-hubspot-api-use-case" : "You want to empower other users in your account to automate specialized parts of their day-to-day processes (e.g., creating highly customized quotes based on the data from an enrolled contact and their associations).", + "x-hubspot-related-documentation" : [ { + "name" : "Custom Workflow Actions Guide", + "url" : "https://developers.hubspot.com/beta-docs/guides/api/automation/custom-workflow-actions" + } ], + "x-hubspot-introduction" : "Use the automation API to define custom actions in the HubSpot workflows tool, which allows users in your account to integrate with external services as they build out a HubSpot workflow." + }, + "servers" : [ { + "url" : "https://api.hubapi.com/automation/v4/actions" + } ], + "tags" : [ { + "name" : "Callbacks" + }, { + "name" : "Definitions" + }, { + "name" : "Functions" + }, { + "name" : "Revisions" + } ], + "paths" : { + "/callbacks/{callbackId}/complete" : { + "post" : { + "tags" : [ "Callbacks" ], + "summary" : "Completes a single callback", + "operationId" : "post-/callbacks/{callbackId}/complete_complete", + "parameters" : [ { + "name" : "callbackId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CallbackCompletionRequest" + } + } + }, + "required" : true + }, + "responses" : { + "204" : { + "description" : "No content", + "content" : { } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "oauth2_legacy" : [ "automation" ] + }, { + "private_apps_legacy" : [ "automation" ] + } ] + } + }, + "/callbacks/complete" : { + "post" : { + "tags" : [ "Callbacks" ], + "summary" : "Completes a batch of callbacks", + "operationId" : "post-/callbacks/complete_completeBatch", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/BatchInputCallbackCompletionBatchRequest" + } + } + }, + "required" : true + }, + "responses" : { + "204" : { + "description" : "No content", + "content" : { } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "oauth2_legacy" : [ "automation" ] + }, { + "private_apps_legacy" : [ "automation" ] + } ] + } + }, + "/{appId}" : { + "get" : { + "tags" : [ "Definitions" ], + "summary" : "Get paged extension definitions", + "operationId" : "get-/{appId}_getPage", + "parameters" : [ { + "name" : "limit", + "in" : "query", + "description" : "The maximum number of results to display per page.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "after", + "in" : "query", + "description" : "The paging cursor token of the last successfully read resource will be returned as the `paging.next.after` JSON property of a paged response containing more results.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "archived", + "in" : "query", + "description" : "Whether to return only results that have been archived.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "boolean", + "default" : false + } + }, { + "name" : "appId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "successful operation", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CollectionResponsePublicActionDefinitionForwardPaging" + } + } + } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "developer_hapikey" : [ ] + } ] + }, + "post" : { + "tags" : [ "Definitions" ], + "summary" : "Create a new extension definition", + "operationId" : "post-/{appId}_create", + "parameters" : [ { + "name" : "appId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PublicActionDefinitionEgg" + } + } + }, + "required" : true + }, + "responses" : { + "201" : { + "description" : "successful operation", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PublicActionDefinition" + } + } + } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "developer_hapikey" : [ ] + } ] + } + }, + "/{appId}/{definitionId}/functions" : { + "get" : { + "tags" : [ "Functions" ], + "summary" : "Get all functions for a given definition", + "operationId" : "get-/{appId}/{definitionId}/functions_getPage", + "parameters" : [ { + "name" : "definitionId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "appId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "successful operation", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CollectionResponsePublicActionFunctionIdentifierNoPaging" + } + } + } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "developer_hapikey" : [ ] + } ] + } + }, + "/{appId}/{definitionId}/functions/{functionType}" : { + "get" : { + "tags" : [ "Functions" ], + "summary" : "Get all functions by a type for a given definition", + "operationId" : "get-/{appId}/{definitionId}/functions/{functionType}_getByFunctionType", + "parameters" : [ { + "name" : "definitionId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "functionType", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string", + "enum" : [ "PRE_ACTION_EXECUTION", "PRE_FETCH_OPTIONS", "POST_FETCH_OPTIONS", "POST_ACTION_EXECUTION" ] + } + }, { + "name" : "appId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "successful operation", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PublicActionFunction" + } + } + } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "developer_hapikey" : [ ] + } ] + }, + "put" : { + "tags" : [ "Functions" ], + "summary" : "Insert a function for a definition", + "operationId" : "put-/{appId}/{definitionId}/functions/{functionType}_createOrReplaceByFunctionType", + "parameters" : [ { + "name" : "definitionId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "functionType", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string", + "enum" : [ "PRE_ACTION_EXECUTION", "PRE_FETCH_OPTIONS", "POST_FETCH_OPTIONS", "POST_ACTION_EXECUTION" ] + } + }, { + "name" : "appId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "requestBody" : { + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + }, + "required" : true + }, + "responses" : { + "200" : { + "description" : "successful operation", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PublicActionFunctionIdentifier" + } + } + } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "developer_hapikey" : [ ] + } ] + }, + "delete" : { + "tags" : [ "Functions" ], + "summary" : "Delete a function for a definition", + "operationId" : "delete-/{appId}/{definitionId}/functions/{functionType}_archiveByFunctionType", + "parameters" : [ { + "name" : "definitionId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "functionType", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string", + "enum" : [ "PRE_ACTION_EXECUTION", "PRE_FETCH_OPTIONS", "POST_FETCH_OPTIONS", "POST_ACTION_EXECUTION" ] + } + }, { + "name" : "appId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "204" : { + "description" : "No content", + "content" : { } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "developer_hapikey" : [ ] + } ] + } + }, + "/{appId}/{definitionId}/revisions/{revisionId}" : { + "get" : { + "tags" : [ "Revisions" ], + "summary" : "Gets a revision for a given definition by revision id", + "operationId" : "get-/{appId}/{definitionId}/revisions/{revisionId}_getById", + "parameters" : [ { + "name" : "definitionId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "revisionId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "appId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "successful operation", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PublicActionRevision" + } + } + } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "developer_hapikey" : [ ] + } ] + } + }, + "/{appId}/{definitionId}" : { + "get" : { + "tags" : [ "Definitions" ], + "summary" : "Get extension definition by Id", + "operationId" : "get-/{appId}/{definitionId}_getById", + "parameters" : [ { + "name" : "definitionId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "archived", + "in" : "query", + "description" : "Whether to return only results that have been archived.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "boolean", + "default" : false + } + }, { + "name" : "appId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "successful operation", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PublicActionDefinition" + } + } + } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "developer_hapikey" : [ ] + } ] + }, + "delete" : { + "tags" : [ "Definitions" ], + "summary" : "Archive an extension definition", + "operationId" : "delete-/{appId}/{definitionId}_archive", + "parameters" : [ { + "name" : "definitionId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "appId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "204" : { + "description" : "No content", + "content" : { } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "developer_hapikey" : [ ] + } ] + }, + "patch" : { + "tags" : [ "Definitions" ], + "summary" : "Patch an existing extension definition", + "operationId" : "patch-/{appId}/{definitionId}_update", + "parameters" : [ { + "name" : "definitionId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "appId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PublicActionDefinitionPatch" + } + } + }, + "required" : true + }, + "responses" : { + "200" : { + "description" : "successful operation", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PublicActionDefinition" + } + } + } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "developer_hapikey" : [ ] + } ] + } + }, + "/{appId}/{definitionId}/functions/{functionType}/{functionId}" : { + "get" : { + "tags" : [ "Functions" ], + "summary" : "Get a function for a given definition", + "operationId" : "get-/{appId}/{definitionId}/functions/{functionType}/{functionId}_getById", + "parameters" : [ { + "name" : "definitionId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "functionType", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string", + "enum" : [ "PRE_ACTION_EXECUTION", "PRE_FETCH_OPTIONS", "POST_FETCH_OPTIONS", "POST_ACTION_EXECUTION" ] + } + }, { + "name" : "functionId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "appId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "successful operation", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PublicActionFunction" + } + } + } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "developer_hapikey" : [ ] + } ] + }, + "put" : { + "tags" : [ "Functions" ], + "summary" : "Insert a function for a definition", + "operationId" : "put-/{appId}/{definitionId}/functions/{functionType}/{functionId}_createOrReplace", + "parameters" : [ { + "name" : "definitionId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "functionType", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string", + "enum" : [ "PRE_ACTION_EXECUTION", "PRE_FETCH_OPTIONS", "POST_FETCH_OPTIONS", "POST_ACTION_EXECUTION" ] + } + }, { + "name" : "functionId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "appId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "requestBody" : { + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + }, + "required" : true + }, + "responses" : { + "200" : { + "description" : "successful operation", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PublicActionFunctionIdentifier" + } + } + } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "developer_hapikey" : [ ] + } ] + }, + "delete" : { + "tags" : [ "Functions" ], + "summary" : "Archive a function for a definition", + "operationId" : "delete-/{appId}/{definitionId}/functions/{functionType}/{functionId}_archive", + "parameters" : [ { + "name" : "definitionId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "functionType", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string", + "enum" : [ "PRE_ACTION_EXECUTION", "PRE_FETCH_OPTIONS", "POST_FETCH_OPTIONS", "POST_ACTION_EXECUTION" ] + } + }, { + "name" : "functionId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "appId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "204" : { + "description" : "No content", + "content" : { } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "developer_hapikey" : [ ] + } ] + } + }, + "/{appId}/{definitionId}/revisions" : { + "get" : { + "tags" : [ "Revisions" ], + "summary" : "Get all revisions for a given definition", + "operationId" : "get-/{appId}/{definitionId}/revisions_getPage", + "parameters" : [ { + "name" : "definitionId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "limit", + "in" : "query", + "description" : "The maximum number of results to display per page.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "after", + "in" : "query", + "description" : "The paging cursor token of the last successfully read resource will be returned as the `paging.next.after` JSON property of a paged response containing more results.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "appId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "successful operation", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CollectionResponsePublicActionRevisionForwardPaging" + } + } + } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "developer_hapikey" : [ ] + } ] + } + } + }, + "components" : { + "schemas" : { + "PublicActionFunction" : { + "required" : [ "functionSource", "functionType" ], + "type" : "object", + "properties" : { + "functionSource" : { + "type" : "string" + }, + "functionType" : { + "type" : "string", + "enum" : [ "PRE_ACTION_EXECUTION", "PRE_FETCH_OPTIONS", "POST_FETCH_OPTIONS", "POST_ACTION_EXECUTION" ] + }, + "id" : { + "type" : "string" + } + } + }, + "PublicActionDefinition" : { + "required" : [ "actionUrl", "functions", "id", "inputFields", "labels", "objectTypes", "published", "revisionId" ], + "type" : "object", + "properties" : { + "functions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/PublicActionFunctionIdentifier" + } + }, + "actionUrl" : { + "type" : "string" + }, + "published" : { + "type" : "boolean" + }, + "labels" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/PublicActionLabels" + } + }, + "inputFields" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/InputFieldDefinition" + } + }, + "outputFields" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/OutputFieldDefinition" + } + }, + "revisionId" : { + "type" : "string" + }, + "archivedAt" : { + "type" : "integer", + "format" : "int64" + }, + "inputFieldDependencies" : { + "type" : "array", + "items" : { + "oneOf" : [ { + "$ref" : "#/components/schemas/PublicSingleFieldDependency" + }, { + "$ref" : "#/components/schemas/PublicConditionalSingleFieldDependency" + } ] + } + }, + "executionRules" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/PublicExecutionTranslationRule" + } + }, + "id" : { + "type" : "string" + }, + "objectTypes" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "objectRequestOptions" : { + "$ref" : "#/components/schemas/PublicObjectRequestOptions" + } + } + }, + "PublicConditionalSingleFieldDependency" : { + "title" : "CONDITIONAL_SINGLE_FIELD", + "required" : [ "controllingFieldName", "controllingFieldValue", "dependencyType", "dependentFieldNames" ], + "type" : "object", + "properties" : { + "dependencyType" : { + "type" : "string", + "enum" : [ "CONDITIONAL_SINGLE_FIELD" ], + "default" : "CONDITIONAL_SINGLE_FIELD" + }, + "controllingFieldName" : { + "type" : "string" + }, + "controllingFieldValue" : { + "type" : "string" + }, + "dependentFieldNames" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + }, + "PublicObjectRequestOptions" : { + "required" : [ "properties" ], + "type" : "object", + "properties" : { + "properties" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + }, + "ErrorDetail" : { + "required" : [ "message" ], + "type" : "object", + "properties" : { + "subCategory" : { + "type" : "string", + "description" : "A specific category that contains more specific detail about the error" + }, + "code" : { + "type" : "string", + "description" : "The status code associated with the error detail" + }, + "in" : { + "type" : "string", + "description" : "The name of the field or parameter in which the error was found." + }, + "context" : { + "type" : "object", + "additionalProperties" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "description" : "Context about the error condition", + "example" : { + "missingScopes" : [ "scope1", "scope2" ] + } + }, + "message" : { + "type" : "string", + "description" : "A human readable message describing the error along with remediation steps where appropriate" + } + } + }, + "PublicActionLabels" : { + "required" : [ "actionName" ], + "type" : "object", + "properties" : { + "inputFieldDescriptions" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + }, + "appDisplayName" : { + "type" : "string" + }, + "outputFieldLabels" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + }, + "inputFieldOptionLabels" : { + "type" : "object", + "additionalProperties" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + } + }, + "actionDescription" : { + "type" : "string" + }, + "executionRules" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + }, + "inputFieldLabels" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + }, + "actionName" : { + "type" : "string" + }, + "actionCardContent" : { + "type" : "string" + } + } + }, + "PublicSingleFieldDependency" : { + "title" : "SINGLE_FIELD", + "required" : [ "controllingFieldName", "dependencyType", "dependentFieldNames" ], + "type" : "object", + "properties" : { + "dependencyType" : { + "type" : "string", + "enum" : [ "SINGLE_FIELD" ], + "default" : "SINGLE_FIELD" + }, + "controllingFieldName" : { + "type" : "string" + }, + "dependentFieldNames" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + }, + "ForwardPaging" : { + "type" : "object", + "properties" : { + "next" : { + "$ref" : "#/components/schemas/NextPage" + } + } + }, + "FieldTypeDefinition" : { + "required" : [ "externalOptions", "name", "options", "type" ], + "type" : "object", + "properties" : { + "helpText" : { + "type" : "string" + }, + "referencedObjectType" : { + "type" : "string", + "enum" : [ "CONTACT", "COMPANY", "DEAL", "ENGAGEMENT", "TICKET", "OWNER", "PRODUCT", "LINE_ITEM", "BET_DELIVERABLE_SERVICE", "CONTENT", "CONVERSATION", "BET_ALERT", "PORTAL", "QUOTE", "FORM_SUBMISSION_INBOUNDDB", "QUOTA", "UNSUBSCRIBE", "COMMUNICATION", "FEEDBACK_SUBMISSION", "ATTRIBUTION", "SALESFORCE_SYNC_ERROR", "RESTORABLE_CRM_OBJECT", "HUB", "LANDING_PAGE", "PRODUCT_OR_FOLDER", "TASK", "FORM", "MARKETING_EMAIL", "AD_ACCOUNT", "AD_CAMPAIGN", "AD_GROUP", "AD", "KEYWORD", "CAMPAIGN", "SOCIAL_CHANNEL", "SOCIAL_POST", "SITE_PAGE", "BLOG_POST", "IMPORT", "EXPORT", "CTA", "TASK_TEMPLATE", "AUTOMATION_PLATFORM_FLOW", "OBJECT_LIST", "NOTE", "MEETING_EVENT", "CALL", "EMAIL", "PUBLISHING_TASK", "CONVERSATION_SESSION", "CONTACT_CREATE_ATTRIBUTION", "INVOICE", "MARKETING_EVENT", "CONVERSATION_INBOX", "CHATFLOW", "MEDIA_BRIDGE", "SEQUENCE", "SEQUENCE_STEP", "FORECAST", "SNIPPET", "TEMPLATE", "DEAL_CREATE_ATTRIBUTION", "QUOTE_TEMPLATE", "QUOTE_MODULE", "QUOTE_MODULE_FIELD", "QUOTE_FIELD", "SEQUENCE_ENROLLMENT", "SUBSCRIPTION", "ACCEPTANCE_TEST", "SOCIAL_BROADCAST", "DEAL_SPLIT", "DEAL_REGISTRATION", "GOAL_TARGET", "GOAL_TARGET_GROUP", "PORTAL_OBJECT_SYNC_MESSAGE", "FILE_MANAGER_FILE", "FILE_MANAGER_FOLDER", "SEQUENCE_STEP_ENROLLMENT", "APPROVAL", "APPROVAL_STEP", "CTA_VARIANT", "SALES_DOCUMENT", "DISCOUNT", "FEE", "TAX", "MARKETING_CALENDAR", "PERMISSIONS_TESTING", "PRIVACY_SCANNER_COOKIE", "DATA_SYNC_STATE", "WEB_INTERACTIVE", "PLAYBOOK", "FOLDER", "PLAYBOOK_QUESTION", "PLAYBOOK_SUBMISSION", "PLAYBOOK_SUBMISSION_ANSWER", "COMMERCE_PAYMENT", "GSC_PROPERTY", "SOX_PROTECTED_DUMMY_TYPE", "BLOG_LISTING_PAGE", "QUARANTINED_SUBMISSION", "PAYMENT_SCHEDULE", "PAYMENT_SCHEDULE_INSTALLMENT", "MARKETING_CAMPAIGN_UTM", "DISCOUNT_TEMPLATE", "DISCOUNT_CODE", "FEEDBACK_SURVEY", "CMS_URL", "SALES_TASK", "SALES_WORKLOAD", "USER", "POSTAL_MAIL", "SCHEMAS_BACKEND_TEST", "PAYMENT_LINK", "SUBMISSION_TAG", "CAMPAIGN_STEP", "SCHEDULING_PAGE", "SOX_PROTECTED_TEST_TYPE", "ORDER", "MARKETING_SMS", "PARTNER_ACCOUNT", "CAMPAIGN_TEMPLATE", "CAMPAIGN_TEMPLATE_STEP", "PLAYLIST", "CLIP", "CAMPAIGN_BUDGET_ITEM", "CAMPAIGN_SPEND_ITEM", "MIC", "CONTENT_AUDIT", "CONTENT_AUDIT_PAGE", "PLAYLIST_FOLDER", "LEAD", "ABANDONED_CART", "EXTERNAL_WEB_URL", "VIEW", "VIEW_BLOCK", "ROSTER", "CART", "AUTOMATION_PLATFORM_FLOW_ACTION", "SOCIAL_PROFILE", "PARTNER_CLIENT", "ROSTER_MEMBER", "MARKETING_EVENT_ATTENDANCE", "ALL_PAGES", "AI_FORECAST", "CRM_PIPELINES_DUMMY_TYPE", "KNOWLEDGE_ARTICLE", "PROPERTY_INFO", "DATA_PRIVACY_CONSENT", "GOAL_TEMPLATE", "SCORE_CONFIGURATION", "AUDIENCE", "PARTNER_CLIENT_REVENUE", "AUTOMATION_JOURNEY", "UNKNOWN" ] + }, + "name" : { + "type" : "string" + }, + "options" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/Option" + } + }, + "description" : { + "type" : "string" + }, + "externalOptionsReferenceType" : { + "type" : "string" + }, + "label" : { + "type" : "string" + }, + "type" : { + "type" : "string", + "enum" : [ "string", "number", "bool", "datetime", "enumeration", "date", "phone_number", "currency_number", "json", "object_coordinates" ] + }, + "fieldType" : { + "type" : "string", + "enum" : [ "booleancheckbox", "checkbox", "date", "file", "number", "phonenumber", "radio", "select", "text", "textarea", "calculation_equation", "calculation_rollup", "calculation_score", "calculation_read_time", "unknown", "html" ] + }, + "optionsUrl" : { + "type" : "string" + }, + "externalOptions" : { + "type" : "boolean" + } + } + }, + "CollectionResponsePublicActionFunctionIdentifierNoPaging" : { + "required" : [ "results" ], + "type" : "object", + "properties" : { + "results" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/PublicActionFunctionIdentifier" + } + } + } + }, + "InputFieldDefinition" : { + "required" : [ "isRequired", "typeDefinition" ], + "type" : "object", + "properties" : { + "isRequired" : { + "type" : "boolean" + }, + "automationFieldType" : { + "type" : "string" + }, + "typeDefinition" : { + "$ref" : "#/components/schemas/FieldTypeDefinition" + }, + "supportedValueTypes" : { + "type" : "array", + "items" : { + "type" : "string", + "enum" : [ "STATIC_VALUE", "OBJECT_PROPERTY", "FIELD_DATA", "FETCHED_OBJECT_PROPERTY", "ENROLLMENT_EVENT_PROPERTY" ] + } + } + } + }, + "PublicExecutionTranslationRule" : { + "required" : [ "conditions", "labelName" ], + "type" : "object", + "properties" : { + "labelName" : { + "type" : "string" + }, + "conditions" : { + "type" : "object", + "additionalProperties" : { + "type" : "object", + "properties" : { } + } + } + } + }, + "PublicActionFunctionIdentifier" : { + "required" : [ "functionType" ], + "type" : "object", + "properties" : { + "functionType" : { + "type" : "string", + "enum" : [ "PRE_ACTION_EXECUTION", "PRE_FETCH_OPTIONS", "POST_FETCH_OPTIONS", "POST_ACTION_EXECUTION" ] + }, + "id" : { + "type" : "string" + } + } + }, + "PublicActionRevision" : { + "required" : [ "createdAt", "definition", "id", "revisionId" ], + "type" : "object", + "properties" : { + "revisionId" : { + "type" : "string" + }, + "createdAt" : { + "type" : "string", + "format" : "date-time" + }, + "definition" : { + "$ref" : "#/components/schemas/PublicActionDefinition" + }, + "id" : { + "type" : "string" + } + } + }, + "CallbackCompletionRequest" : { + "required" : [ "outputFields" ], + "type" : "object", + "properties" : { + "outputFields" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + } + } + }, + "CollectionResponsePublicActionDefinitionForwardPaging" : { + "required" : [ "results" ], + "type" : "object", + "properties" : { + "paging" : { + "$ref" : "#/components/schemas/ForwardPaging" + }, + "results" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/PublicActionDefinition" + } + } + } + }, + "Error" : { + "required" : [ "category", "correlationId", "message" ], + "type" : "object", + "properties" : { + "subCategory" : { + "type" : "string", + "description" : "A specific category that contains more specific detail about the error" + }, + "context" : { + "type" : "object", + "additionalProperties" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "description" : "Context about the error condition", + "example" : { + "missingScopes" : [ "scope1", "scope2" ], + "invalidPropertyName" : [ "propertyValue" ] + } + }, + "correlationId" : { + "type" : "string", + "description" : "A unique identifier for the request. Include this value with any error reports or support tickets", + "format" : "uuid", + "example" : "aeb5f871-7f07-4993-9211-075dc63e7cbf" + }, + "links" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + }, + "description" : "A map of link names to associated URIs containing documentation about the error or recommended remediation steps", + "example" : { + "knowledge-base" : "https://www.hubspot.com/products/service/knowledge-base" + } + }, + "message" : { + "type" : "string", + "description" : "A human readable message describing the error along with remediation steps where appropriate", + "example" : "Invalid input (details will vary based on the error)" + }, + "category" : { + "type" : "string", + "description" : "The error category", + "example" : "VALIDATION_ERROR" + }, + "errors" : { + "type" : "array", + "description" : "further information about the error", + "items" : { + "$ref" : "#/components/schemas/ErrorDetail" + } + } + }, + "example" : { + "message" : "Invalid input (details will vary based on the error)", + "correlationId" : "aeb5f871-7f07-4993-9211-075dc63e7cbf", + "category" : "VALIDATION_ERROR", + "links" : { + "knowledge-base" : "https://www.hubspot.com/products/service/knowledge-base" + } + } + }, + "CollectionResponsePublicActionRevisionForwardPaging" : { + "required" : [ "results" ], + "type" : "object", + "properties" : { + "paging" : { + "$ref" : "#/components/schemas/ForwardPaging" + }, + "results" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/PublicActionRevision" + } + } + } + }, + "CallbackCompletionBatchRequest" : { + "required" : [ "callbackId", "outputFields" ], + "type" : "object", + "properties" : { + "outputFields" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + }, + "callbackId" : { + "type" : "string" + } + } + }, + "PublicActionDefinitionEgg" : { + "required" : [ "actionUrl", "functions", "inputFields", "labels", "objectTypes", "published" ], + "type" : "object", + "properties" : { + "inputFields" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/InputFieldDefinition" + } + }, + "outputFields" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/OutputFieldDefinition" + } + }, + "archivedAt" : { + "type" : "integer", + "format" : "int64" + }, + "functions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/PublicActionFunction" + } + }, + "actionUrl" : { + "type" : "string" + }, + "inputFieldDependencies" : { + "type" : "array", + "items" : { + "oneOf" : [ { + "$ref" : "#/components/schemas/PublicSingleFieldDependency" + }, { + "$ref" : "#/components/schemas/PublicConditionalSingleFieldDependency" + } ] + } + }, + "published" : { + "type" : "boolean" + }, + "executionRules" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/PublicExecutionTranslationRule" + } + }, + "objectTypes" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "objectRequestOptions" : { + "$ref" : "#/components/schemas/PublicObjectRequestOptions" + }, + "labels" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/PublicActionLabels" + } + } + } + }, + "PublicActionDefinitionPatch" : { + "type" : "object", + "properties" : { + "inputFields" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/InputFieldDefinition" + } + }, + "outputFields" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/OutputFieldDefinition" + } + }, + "actionUrl" : { + "type" : "string" + }, + "inputFieldDependencies" : { + "type" : "array", + "items" : { + "oneOf" : [ { + "$ref" : "#/components/schemas/PublicSingleFieldDependency" + }, { + "$ref" : "#/components/schemas/PublicConditionalSingleFieldDependency" + } ] + } + }, + "published" : { + "type" : "boolean" + }, + "executionRules" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/PublicExecutionTranslationRule" + } + }, + "objectTypes" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "objectRequestOptions" : { + "$ref" : "#/components/schemas/PublicObjectRequestOptions" + }, + "labels" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/PublicActionLabels" + } + } + } + }, + "BatchInputCallbackCompletionBatchRequest" : { + "required" : [ "inputs" ], + "type" : "object", + "properties" : { + "inputs" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/CallbackCompletionBatchRequest" + } + } + } + }, + "Option" : { + "required" : [ "description", "displayOrder", "doubleData", "hidden", "label", "readOnly", "value" ], + "type" : "object", + "properties" : { + "hidden" : { + "type" : "boolean" + }, + "displayOrder" : { + "type" : "integer", + "format" : "int32" + }, + "doubleData" : { + "type" : "number" + }, + "description" : { + "type" : "string" + }, + "readOnly" : { + "type" : "boolean" + }, + "label" : { + "type" : "string" + }, + "value" : { + "type" : "string" + } + } + }, + "OutputFieldDefinition" : { + "required" : [ "typeDefinition" ], + "type" : "object", + "properties" : { + "typeDefinition" : { + "$ref" : "#/components/schemas/FieldTypeDefinition" + } + } + }, + "NextPage" : { + "required" : [ "after" ], + "type" : "object", + "properties" : { + "link" : { + "type" : "string" + }, + "after" : { + "type" : "string" + } + } + } + }, + "responses" : { + "Error" : { + "description" : "An error occurred.", + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/Error" + } + } + } + } + }, + "securitySchemes" : { + "oauth2_legacy" : { + "type" : "oauth2", + "flows" : { + "authorizationCode" : { + "authorizationUrl" : "https://app.hubspot.com/oauth/authorize", + "tokenUrl" : "https://api.hubapi.com/oauth/v1/token", + "scopes" : { + "automation" : "Read from and write to my Workflows" + } + } + } + }, + "developer_hapikey" : { + "type" : "apiKey", + "name" : "hapikey", + "in" : "query" + }, + "private_apps_legacy" : { + "type" : "apiKey", + "name" : "private-app-legacy", + "in" : "header" + } + } + }, + "x-hubspot-available-client-libraries" : [ "PHP", "Node", "Ruby", "Python" ], + "x-hubspot-product-tier-requirements" : { + "marketing" : "PROFESSIONAL", + "sales" : "PROFESSIONAL", + "service" : "PROFESSIONAL" + }, + "x-hubspot-documentation-banner" : "NONE" +} diff --git a/docs/spec/sanitations.md b/docs/spec/sanitations.md index c6c18ba..af822d8 100644 --- a/docs/spec/sanitations.md +++ b/docs/spec/sanitations.md @@ -9,16 +9,25 @@ This document records the sanitation done on top of the official OpenAPI specifi The OpenAPI specification is obtained from (TODO: Add source link). These changes are done in order to improve the overall usability, and as workarounds for some known language limitations. -[//]: # (TODO: Add sanitation details) -1. -2. -3. + +1. **Change the `url` property of the `servers` object**: + - **Original**: `https://api.hubapi.com` + - **Updated**: `https://api.hubapi.com/automation/v4/actions` + - **Reason**: This change is made to ensure that all API paths are relative to the URL (`/automation/v4/actions`), which improves the consistency and usability of the APIs. + + +2. **Update API Paths**: + - **Original**: Paths shared a common segment across all resource endpoints. + - **Updated**: Common paths segment is removed from the resource endpoints, as it is now included in the base URL. For example: + - **Original**: `/automation/v4/actions` + - **Updated**: `/` + - **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. ## OpenAPI cli command The following command was used to generate the Ballerina client from the OpenAPI specification. The command should be executed from the repository root directory. ```bash -# TODO: Add OpenAPI CLI command used to generate the client +bal openapi -i docs/spec/openapi.yaml --mode client --license docs/license.txt -o ballerina ``` Note: The license year is hardcoded to 2024, change if necessary. diff --git a/examples/README.md b/examples/README.md index 8a117a8..e3a8593 100644 --- a/examples/README.md +++ b/examples/README.md @@ -2,13 +2,20 @@ The `ballerinax/hubspot.automation.actions` connector provides practical examples illustrating usage in various scenarios. -[//]: # (TODO: Add examples) -1. -2. +1.[Extension Defintion CRUD](examples/extension-crud/main.bal) + +2.[Callback Completion](examples/callback-completion/main.bal) ## Prerequisites -[//]: # (TODO: Add prerequisites) +1. Generate Credentials to authenticate the connector as described in the [Setup Guide](README.md) + +2. For each example create a Config.toml following the [Config.toml.template] + +``` +apiKey="" +oauthKey="" +``` ## Running an example diff --git a/examples/callback-completion/Ballerina.toml b/examples/callback-completion/Ballerina.toml new file mode 100644 index 0000000..263230e --- /dev/null +++ b/examples/callback-completion/Ballerina.toml @@ -0,0 +1,11 @@ + +[package] +org = "wso2" +name = "callback_completion" +version = "0.1.0" + +[[dependency]] +org = "ballerinax" +name = "hubspot.automation.actions" +version = "1.0.0" +repository = "local" diff --git a/examples/callback-completion/Dependencies.toml b/examples/callback-completion/Dependencies.toml new file mode 100644 index 0000000..06cbafb --- /dev/null +++ b/examples/callback-completion/Dependencies.toml @@ -0,0 +1,302 @@ +# AUTO-GENERATED FILE. DO NOT MODIFY. + +# This file is auto-generated by Ballerina for managing dependency versions. +# It should not be modified by hand. + +[ballerina] +dependencies-toml-version = "2" +distribution-version = "2201.10.3" + +[[package]] +org = "ballerina" +name = "auth" +version = "2.12.0" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.array"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "log"} +] + +[[package]] +org = "ballerina" +name = "cache" +version = "3.8.0" +dependencies = [ + {org = "ballerina", name = "constraint"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "task"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "constraint" +version = "1.5.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "crypto" +version = "2.7.2" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "file" +version = "1.10.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "os"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "http" +version = "2.12.4" +dependencies = [ + {org = "ballerina", name = "auth"}, + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "constraint"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "file"}, + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "jwt"}, + {org = "ballerina", name = "lang.array"}, + {org = "ballerina", name = "lang.decimal"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "lang.regexp"}, + {org = "ballerina", name = "lang.runtime"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "lang.value"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "mime"}, + {org = "ballerina", name = "oauth2"}, + {org = "ballerina", name = "observe"}, + {org = "ballerina", name = "time"}, + {org = "ballerina", name = "url"} +] + +[[package]] +org = "ballerina" +name = "io" +version = "1.6.3" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"} +] + +[[package]] +org = "ballerina" +name = "jballerina.java" +version = "0.0.0" + +[[package]] +org = "ballerina" +name = "jwt" +version = "2.13.0" +dependencies = [ + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "lang.__internal" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.object"} +] + +[[package]] +org = "ballerina" +name = "lang.array" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.__internal"} +] + +[[package]] +org = "ballerina" +name = "lang.decimal" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.int" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.__internal"}, + {org = "ballerina", name = "lang.object"} +] + +[[package]] +org = "ballerina" +name = "lang.object" +version = "0.0.0" + +[[package]] +org = "ballerina" +name = "lang.regexp" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.runtime" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.string" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.regexp"} +] + +[[package]] +org = "ballerina" +name = "lang.value" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "log" +version = "2.10.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"}, + {org = "ballerina", name = "observe"} +] + +[[package]] +org = "ballerina" +name = "mime" +version = "2.10.1" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "log"} +] + +[[package]] +org = "ballerina" +name = "oauth2" +version = "2.12.0" +dependencies = [ + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "time"}, + {org = "ballerina", name = "url"} +] + +[[package]] +org = "ballerina" +name = "observe" +version = "1.3.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "os" +version = "1.8.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "task" +version = "2.5.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "time" +version = "2.5.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "url" +version = "2.4.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerinai" +name = "observe" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "observe"} +] + +[[package]] +org = "ballerinax" +name = "hubspot.automation.actions" +version = "1.0.0" +dependencies = [ + {org = "ballerina", name = "http"}, + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "url"}, + {org = "ballerinai", name = "observe"} +] +modules = [ + {org = "ballerinax", packageName = "hubspot.automation.actions", moduleName = "hubspot.automation.actions"} +] + +[[package]] +org = "wso2" +name = "callback_completion" +version = "0.1.0" +dependencies = [ + {org = "ballerinax", name = "hubspot.automation.actions"} +] +modules = [ + {org = "wso2", packageName = "callback_completion", moduleName = "callback_completion"} +] + diff --git a/examples/callback-completion/README.md b/examples/callback-completion/README.md new file mode 100644 index 0000000..810790e --- /dev/null +++ b/examples/callback-completion/README.md @@ -0,0 +1,21 @@ +## Batch Callback Completion + +This example demonstrates how to handle batch callback completion using the HubSpot Automation Actions connector. + +## Prerequisites + +1. Generate Credentials to authenticate the connector as described in the [Setup Guide](../README.md) + +2. For each example create a Config.toml following the [Config.toml.template] + +```bash +oauthKey="" +``` + +## Run the example + +Execute the following command to run the example: + +```bash +bal run +``` diff --git a/examples/callback-completion/main.bal b/examples/callback-completion/main.bal new file mode 100644 index 0000000..d17fae6 --- /dev/null +++ b/examples/callback-completion/main.bal @@ -0,0 +1,42 @@ +// Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerinax/hubspot.automation.actions; + +configurable string oauthKey = ?; + +public function main() returns error? { + // BearerTokenConfig + actions:ConnectionConfig oauthConfig = { + auth: { + token: oauthKey + } + }; + + final actions:Client automationClient = check new actions:Client(oauthConfig); + + actions:BatchInputCallbackCompletionBatchRequest batchCallbackCompletionRequest = { + inputs: [ + { + callbackId: "1", + outputFields: { + "exampleField": "exampleValue" + } + } + ] + }; + _ = check automationClient->/callbacks/complete.post(batchCallbackCompletionRequest); +} diff --git a/examples/extension-crud/Ballerina.toml b/examples/extension-crud/Ballerina.toml new file mode 100644 index 0000000..2f84e00 --- /dev/null +++ b/examples/extension-crud/Ballerina.toml @@ -0,0 +1,10 @@ +[package] +org = "wso2" +name = "extensioncrud" +version = "0.1.0" + +[[dependency]] +org = "ballerinax" +name = "hubspot.automation.actions" +version = "1.0.0" +repository = "local" diff --git a/examples/extension-crud/Dependencies.toml b/examples/extension-crud/Dependencies.toml new file mode 100644 index 0000000..7d7adcf --- /dev/null +++ b/examples/extension-crud/Dependencies.toml @@ -0,0 +1,311 @@ +# AUTO-GENERATED FILE. DO NOT MODIFY. + +# This file is auto-generated by Ballerina for managing dependency versions. +# It should not be modified by hand. + +[ballerina] +dependencies-toml-version = "2" +distribution-version = "2201.10.3" + +[[package]] +org = "ballerina" +name = "auth" +version = "2.12.0" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.array"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "log"} +] + +[[package]] +org = "ballerina" +name = "cache" +version = "3.8.0" +dependencies = [ + {org = "ballerina", name = "constraint"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "task"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "constraint" +version = "1.5.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "crypto" +version = "2.7.2" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "file" +version = "1.10.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "os"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "http" +version = "2.12.4" +dependencies = [ + {org = "ballerina", name = "auth"}, + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "constraint"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "file"}, + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "jwt"}, + {org = "ballerina", name = "lang.array"}, + {org = "ballerina", name = "lang.decimal"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "lang.regexp"}, + {org = "ballerina", name = "lang.runtime"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "lang.value"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "mime"}, + {org = "ballerina", name = "oauth2"}, + {org = "ballerina", name = "observe"}, + {org = "ballerina", name = "time"}, + {org = "ballerina", name = "url"} +] +modules = [ + {org = "ballerina", packageName = "http", moduleName = "http"}, + {org = "ballerina", packageName = "http", moduleName = "http.httpscerr"} +] + +[[package]] +org = "ballerina" +name = "io" +version = "1.6.3" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"} +] +modules = [ + {org = "ballerina", packageName = "io", moduleName = "io"} +] + +[[package]] +org = "ballerina" +name = "jballerina.java" +version = "0.0.0" + +[[package]] +org = "ballerina" +name = "jwt" +version = "2.13.0" +dependencies = [ + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "lang.__internal" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.object"} +] + +[[package]] +org = "ballerina" +name = "lang.array" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.__internal"} +] + +[[package]] +org = "ballerina" +name = "lang.decimal" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.int" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.__internal"}, + {org = "ballerina", name = "lang.object"} +] + +[[package]] +org = "ballerina" +name = "lang.object" +version = "0.0.0" + +[[package]] +org = "ballerina" +name = "lang.regexp" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.runtime" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.string" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.regexp"} +] + +[[package]] +org = "ballerina" +name = "lang.value" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "log" +version = "2.10.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"}, + {org = "ballerina", name = "observe"} +] + +[[package]] +org = "ballerina" +name = "mime" +version = "2.10.1" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "log"} +] + +[[package]] +org = "ballerina" +name = "oauth2" +version = "2.12.0" +dependencies = [ + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "time"}, + {org = "ballerina", name = "url"} +] + +[[package]] +org = "ballerina" +name = "observe" +version = "1.3.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "os" +version = "1.8.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "task" +version = "2.5.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "time" +version = "2.5.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "url" +version = "2.4.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerinai" +name = "observe" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "observe"} +] + +[[package]] +org = "ballerinax" +name = "hubspot.automation.actions" +version = "1.0.0" +dependencies = [ + {org = "ballerina", name = "http"}, + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "url"}, + {org = "ballerinai", name = "observe"} +] +modules = [ + {org = "ballerinax", packageName = "hubspot.automation.actions", moduleName = "hubspot.automation.actions"} +] + +[[package]] +org = "wso2" +name = "extensioncrud" +version = "0.1.0" +dependencies = [ + {org = "ballerina", name = "http"}, + {org = "ballerina", name = "io"}, + {org = "ballerinax", name = "hubspot.automation.actions"} +] +modules = [ + {org = "wso2", packageName = "extensioncrud", moduleName = "extensioncrud"} +] + diff --git a/examples/extension-crud/README.md b/examples/extension-crud/README.md new file mode 100644 index 0000000..6c91cd4 --- /dev/null +++ b/examples/extension-crud/README.md @@ -0,0 +1,21 @@ +## Extension Definition CRUD Example + +This example demonstrates how to perform CRUD (Create, Read, Update, Delete) operations on the Extension Definition Entity using the HubSpot Automation Actions module in Ballerina. + +## Prerequisites + +1. Generate Credentials to authenticate the connector as described in the [Setup Guide](../README.md) + +2. For each example create a Config.toml following the [Config.toml.template] + +```bash +apiKey="" +``` + +## Run the example + +Execute the following command to run the example: + +```bash +bal run +``` diff --git a/examples/extension-crud/main.bal b/examples/extension-crud/main.bal new file mode 100644 index 0000000..79b1024 --- /dev/null +++ b/examples/extension-crud/main.bal @@ -0,0 +1,104 @@ +// Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/io; +import ballerina/http; +import ballerinax/hubspot.automation.actions; + +configurable string apiKey = ?; + +public function main() returns error? { + + // Developer API Key Config + actions:ConnectionConfig apikeyConfig = { + auth: { + + hapikey: apiKey, + private\-app\-legacy: "" + } + }; + + // Client initialization + final actions:Client hubspotAutomation = check new actions:Client(apikeyConfig); + + // sample extension definition + string createdExtensionId = ""; + int:Signed32 appId = 5712614; + + actions:FieldTypeDefinition typeDefinition = { + referencedObjectType: "OWNER", + externalOptions: false, + externalOptionsReferenceType: "", + name: "optionsInput", + 'type: "enumeration", + fieldType: "select", + optionsUrl: "https://webhook.site/94d09471-6f4c-4a7f-bae2-c9a585dd41e0", + options: [] + }; + + actions:InputFieldDefinition inputFieldDefinition = { + isRequired: true, + automationFieldType: "", + typeDefinition: typeDefinition, + supportedValueTypes: ["STATIC_VALUE"] + }; + + actions:PublicActionFunction publicActionFunction = { + functionSource: "exports.main = (event, callback) => {\r\n callback({\r\n outputFields: {\r\n myOutput: \"example output value\"\r\n }\r\n });\r\n}", + functionType: "POST_ACTION_EXECUTION" + }; + + actions:PublicActionDefinitionEgg testingPublicActionDefinitionEgg = { + inputFields: [inputFieldDefinition], + actionUrl: "https://webhook.site/94d09471-6f4c-4a7f-bae2-c9a585dd41e0", + published: false, + objectTypes: ["CONTACT"], + objectRequestOptions: {properties: ["email"]}, + functions: [publicActionFunction], + labels: { + "en": { + inputFieldLabels: { + "staticInput": "Static Input", + "objectInput": "Object Property Input", + "optionsInput": "External Options Input" + }, + actionName: "My Extension", + actionDescription: "My Extension Description", + appDisplayName: "My App Display Name", + actionCardContent: "My Action Card Content" + } + } + }; + + // Create Extension + actions:PublicActionDefinition response = check hubspotAutomation->/[appId].post(testingPublicActionDefinitionEgg); + createdExtensionId = response.id; + io:println("Extension Created with ID: " + createdExtensionId); + + // Get Extension + actions:PublicActionDefinition getResponse = check hubspotAutomation->/[appId]/[createdExtensionId].get(); + io:println("Extension Retrieved: " + getResponse.id); + + // Update Extension + actions:PublicActionDefinition updateResponse = check hubspotAutomation->/[appId]/[createdExtensionId]; + io:println("Extension Updated: "); + io:println(updateResponse); + + // Delete Extension + http:Response deleteResponse = check hubspotAutomation->/[appId]/[createdExtensionId].delete(); + io:println("Extension Deleted"); + +} diff --git a/image.png b/image.png new file mode 100644 index 0000000..3589916 Binary files /dev/null and b/image.png differ