From 3968779b0c98d67455bc4a8c2dc88a2292e38167 Mon Sep 17 00:00:00 2001 From: Nikolina Spehar Date: Wed, 9 Jul 2025 10:15:06 +0200 Subject: [PATCH 1/3] 2785 - Amplitude component added --- .../docs/reference/components/amplitude.mdx | 156 ++ server/apps/server-app/build.gradle.kts | 1 + server/ee/apps/worker-app/build.gradle.kts | 1 + .../components/amplitude/build.gradle.kts | 2 + .../amplitude/AmplitudeComponentHandler.java | 55 + ...AmplitudeCreateAttributionEventAction.java | 99 ++ .../AmplitudeCreateOrUpdateUserAction.java | 150 ++ .../connection/AmplitudeConnection.java | 54 + .../constant/AmplitudeConstants.java | 72 + .../amplitude/util/AmplitudeUtils.java | 98 ++ .../amplitude/src/main/resources/README.mdx | 0 .../src/main/resources/assets/amplitude.svg | 25 + .../AmplitudeComponentHandlerTest.java | 31 + ...itudeCreateAttributionEventActionTest.java | 99 ++ ...AmplitudeCreateOrUpdateUserActionTest.java | 109 ++ .../amplitude/util/AmplitudeUtilsTest.java | 109 ++ .../resources/definition/amplitude_v1.json | 1399 +++++++++++++++++ settings.gradle.kts | 1 + 18 files changed, 2461 insertions(+) create mode 100644 docs/content/docs/reference/components/amplitude.mdx create mode 100644 server/libs/modules/components/amplitude/build.gradle.kts create mode 100644 server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/AmplitudeComponentHandler.java create mode 100644 server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/action/AmplitudeCreateAttributionEventAction.java create mode 100644 server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/action/AmplitudeCreateOrUpdateUserAction.java create mode 100644 server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/connection/AmplitudeConnection.java create mode 100644 server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/constant/AmplitudeConstants.java create mode 100644 server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/util/AmplitudeUtils.java create mode 100644 server/libs/modules/components/amplitude/src/main/resources/README.mdx create mode 100644 server/libs/modules/components/amplitude/src/main/resources/assets/amplitude.svg create mode 100644 server/libs/modules/components/amplitude/src/test/java/com/bytechef/component/amplitude/AmplitudeComponentHandlerTest.java create mode 100644 server/libs/modules/components/amplitude/src/test/java/com/bytechef/component/amplitude/action/AmplitudeCreateAttributionEventActionTest.java create mode 100644 server/libs/modules/components/amplitude/src/test/java/com/bytechef/component/amplitude/action/AmplitudeCreateOrUpdateUserActionTest.java create mode 100644 server/libs/modules/components/amplitude/src/test/java/com/bytechef/component/amplitude/util/AmplitudeUtilsTest.java create mode 100644 server/libs/modules/components/amplitude/src/test/resources/definition/amplitude_v1.json diff --git a/docs/content/docs/reference/components/amplitude.mdx b/docs/content/docs/reference/components/amplitude.mdx new file mode 100644 index 00000000000..7e81cbe528c --- /dev/null +++ b/docs/content/docs/reference/components/amplitude.mdx @@ -0,0 +1,156 @@ +--- +title: "Amplitude" +description: "Build better products by turning your user data into meaningful insights, using Amplitude's digital analytics platform and experimentation tools." +--- + + +Categories: Analytics + + +Type: amplitude/v1 + +
+ + + +## Connections + +Version: 1 + + +### custom + +#### Properties + +| Name | Label | Type | Description | Required | +|:---------------:|:--------------:|:------------:|:-------------------:|:--------:| +| api_key | API Key | STRING | `The API key for the Amplitude project` | true | +| region | Region | STRING
Options api2, api.eu
| `Environment region you wish to access the API in.` | true | + + + + + +
+ + + +## Actions + + +### Create Attribution Event +Name: createAttributionEvent + +`Creates attribution event using Attribution API.` + +#### Properties + +| Name | Label | Type | Description | Required | +|:---------------:|:--------------:|:------------:|:-------------------:|:--------:| +| event_type | Event Type | STRING | `The event info. Prefix with brackets [YOUR COMPANY].` | true | +| platform | Platform | STRING
Options ios, android
| `Platform which the event will occur on.` | true | +| identifier | Identifier | OBJECT
Properties {STRING\(key), STRING\(value)}
| `Identifier of the platform.` | true | +| user_properties | User Properties | ARRAY
Items [{STRING\(key), STRING\(value)}\($property)]
| `A dictionary of attribution properties prefixed with brackets [YOUR COMPANY].` | false | + +#### Example JSON Structure +```json +{ + "label" : "Create Attribution Event", + "name" : "createAttributionEvent", + "parameters" : { + "event_type" : "", + "platform" : "", + "identifier" : { + "key" : "", + "value" : "" + }, + "user_properties" : [ { + "key" : "", + "value" : "" + } ] + }, + "type" : "amplitude/v1/createAttributionEvent" +} +``` + +#### Output + + + +Type: STRING + + + + + + + + +### Create or Update User +Name: createOrUpdateUser + +`Creates or updates user without sending an event.` + +#### Properties + +| Name | Label | Type | Description | Required | +|:---------------:|:--------------:|:------------:|:-------------------:|:--------:| +| id | ID | STRING
Options user_id, device_id
| `Choose to create or update a user or a device.` | true | +| device_id | Device ID | STRING | `A device specific identifier, such as the Identifier for Vendor (IDFV) on iOS.` | true | +| user_id | User ID | STRING | `Unique user ID specified by you. If you send a request with a user ID that's not in the Amplitude system, new user will be created (e.g. email address).` | true | +| user_properties | User Properties | ARRAY
Items [{STRING\(key), STRING\(value)}\($property)]
| `A dictionary of attribution properties prefixed with brackets [YOUR COMPANY].` | false | +| platform | Platform | STRING | `The platform that's sending the data.` | false | +| os_name | Operating System Name | STRING | `The mobile operating system or browser the user is on.` | false | +| device_brand | Device Brand | STRING | `The device brand the user is on.` | false | +| carrier | Carrier | STRING | `The carrier of the device the user is on.` | false | +| country | Country | STRING | `The country the user is in.` | false | +| city | City | STRING | `The city the user is in.` | false | +| dma | Designated Market Area | STRING | `The Designated Market Area of the user.` | false | +| language | Language | STRING | `The language the user has set.` | false | + +#### Example JSON Structure +```json +{ + "label" : "Create or Update User", + "name" : "createOrUpdateUser", + "parameters" : { + "id" : "", + "device_id" : "", + "user_id" : "", + "user_properties" : [ { + "key" : "", + "value" : "" + } ], + "platform" : "", + "os_name" : "", + "device_brand" : "", + "carrier" : "", + "country" : "", + "city" : "", + "dma" : "", + "language" : "" + }, + "type" : "amplitude/v1/createOrUpdateUser" +} +``` + +#### Output + + + +Type: STRING + + + + + + + + + + +
+ +# Additional instructions +
+ diff --git a/server/apps/server-app/build.gradle.kts b/server/apps/server-app/build.gradle.kts index 428efb88e13..958e91e5725 100644 --- a/server/apps/server-app/build.gradle.kts +++ b/server/apps/server-app/build.gradle.kts @@ -156,6 +156,7 @@ dependencies { implementation(project(":server:libs:modules:components:ai:vectorstore:weaviate")) implementation(project(":server:libs:modules:components:airtable")) implementation(project(":server:libs:modules:components:aitable")) + implementation(project(":server:libs:modules:components:amplitude")) implementation(project(":server:libs:modules:components:apify")) implementation(project(":server:libs:modules:components:apollo")) implementation(project(":server:libs:modules:components:app-event")) diff --git a/server/ee/apps/worker-app/build.gradle.kts b/server/ee/apps/worker-app/build.gradle.kts index 7b9e29443d1..82e4150834e 100644 --- a/server/ee/apps/worker-app/build.gradle.kts +++ b/server/ee/apps/worker-app/build.gradle.kts @@ -98,6 +98,7 @@ dependencies { implementation(project(":server:libs:modules:components:ai:vectorstore:weaviate")) implementation(project(":server:libs:modules:components:airtable")) implementation(project(":server:libs:modules:components:aitable")) + implementation(project(":server:libs:modules:components:amplitude")) implementation(project(":server:libs:modules:components:apify")) implementation(project(":server:libs:modules:components:apollo")) implementation(project(":server:libs:modules:components:app-event")) diff --git a/server/libs/modules/components/amplitude/build.gradle.kts b/server/libs/modules/components/amplitude/build.gradle.kts new file mode 100644 index 00000000000..bf2a7f56ed1 --- /dev/null +++ b/server/libs/modules/components/amplitude/build.gradle.kts @@ -0,0 +1,2 @@ +version="1.0" + diff --git a/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/AmplitudeComponentHandler.java b/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/AmplitudeComponentHandler.java new file mode 100644 index 00000000000..7bd5f1982a5 --- /dev/null +++ b/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/AmplitudeComponentHandler.java @@ -0,0 +1,55 @@ +/* + * Copyright 2025 ByteChef + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.bytechef.component.amplitude; + +import static com.bytechef.component.definition.ComponentDsl.component; +import static com.bytechef.component.definition.ComponentDsl.tool; + +import com.bytechef.component.ComponentHandler; +import com.bytechef.component.amplitude.action.AmplitudeCreateAttributionEventAction; +import com.bytechef.component.amplitude.action.AmplitudeCreateOrUpdateUserAction; +import com.bytechef.component.amplitude.connection.AmplitudeConnection; +import com.bytechef.component.definition.ComponentCategory; +import com.bytechef.component.definition.ComponentDefinition; +import com.google.auto.service.AutoService; + +/** + * @author Nikolina Spehar + */ +@AutoService(ComponentHandler.class) +public class AmplitudeComponentHandler implements ComponentHandler { + + private static final ComponentDefinition COMPONENT_DEFINITION = component("amplitude") + .title("Amplitude") + .description( + "Build better products by turning your user data into meaningful insights, using Amplitude's digital " + + "analytics platform and experimentation tools.") + .icon("path:assets/amplitude.svg") + .categories(ComponentCategory.ANALYTICS) + .connection(AmplitudeConnection.CONNECTION_DEFINITION) + .actions( + AmplitudeCreateAttributionEventAction.ACTION_DEFINITION, + AmplitudeCreateOrUpdateUserAction.ACTION_DEFINITION) + .clusterElements( + tool(AmplitudeCreateAttributionEventAction.ACTION_DEFINITION), + tool(AmplitudeCreateOrUpdateUserAction.ACTION_DEFINITION)); + + @Override + public ComponentDefinition getDefinition() { + return COMPONENT_DEFINITION; + } +} diff --git a/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/action/AmplitudeCreateAttributionEventAction.java b/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/action/AmplitudeCreateAttributionEventAction.java new file mode 100644 index 00000000000..5f7ce5c5735 --- /dev/null +++ b/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/action/AmplitudeCreateAttributionEventAction.java @@ -0,0 +1,99 @@ +/* + * Copyright 2025 ByteChef + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.bytechef.component.amplitude.action; + +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.API_KEY; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.EVENT; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.EVENT_TYPE; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.IDENTIFIER; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.KEY; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.PLATFORM; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.USER_PROPERTIES_OBJECT; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.VALUE; +import static com.bytechef.component.amplitude.util.AmplitudeUtils.getEventJson; +import static com.bytechef.component.definition.ComponentDsl.action; +import static com.bytechef.component.definition.ComponentDsl.object; +import static com.bytechef.component.definition.ComponentDsl.option; +import static com.bytechef.component.definition.ComponentDsl.outputSchema; +import static com.bytechef.component.definition.ComponentDsl.string; +import static com.bytechef.component.definition.Context.Http.responseType; + +import com.bytechef.component.amplitude.util.AmplitudeUtils; +import com.bytechef.component.definition.ComponentDsl.ModifiableActionDefinition; +import com.bytechef.component.definition.Context; +import com.bytechef.component.definition.Context.Http.ResponseType; +import com.bytechef.component.definition.OptionsDataSource.ActionOptionsFunction; +import com.bytechef.component.definition.Parameters; +import com.bytechef.component.definition.TypeReference; + +/** + * @author Nikolina Spehar + */ +public class AmplitudeCreateAttributionEventAction { + + public static final ModifiableActionDefinition ACTION_DEFINITION = action("createAttributionEvent") + .title("Create Attribution Event") + .description("Creates attribution event using Attribution API.") + .properties( + string(EVENT_TYPE) + .label("Event Type") + .description("The event info. Prefix with brackets [YOUR COMPANY].") + .required(true), + string(PLATFORM) + .label("Platform") + .description("Platform which the event will occur on.") + .options( + option("iOS", "ios"), + option("Android", "android")) + .required(true), + object(IDENTIFIER) + .label("Identifier") + .description("Identifier of the platform.") + .required(true) + .properties( + string(KEY) + .label("Identifier Key") + .description("For iOS input the Identifier for Advertiser or the Identifier for Vendor." + + "For Android input the Google ADID or App Set ID") + .options((ActionOptionsFunction) AmplitudeUtils::getIdentifierKeyOptions) + .optionsLookupDependsOn(PLATFORM) + .required(true), + string(VALUE) + .label("Identifier Value") + .description("Value of selected identifier.") + .required(true)), + USER_PROPERTIES_OBJECT) + .output( + outputSchema( + string() + .description("Response message"))) + .perform(AmplitudeCreateAttributionEventAction::perform); + + private AmplitudeCreateAttributionEventAction() { + } + + public static String perform(Parameters inputParameters, Parameters connectionParameters, Context context) { + + return context.http(http -> http.post("/attribution")) + .configuration(responseType(ResponseType.TEXT)) + .queryParameters( + API_KEY, connectionParameters.getRequiredString(API_KEY), + EVENT, getEventJson(inputParameters, context)) + .execute() + .getBody(new TypeReference<>() {}); + } +} diff --git a/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/action/AmplitudeCreateOrUpdateUserAction.java b/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/action/AmplitudeCreateOrUpdateUserAction.java new file mode 100644 index 00000000000..e2879205c43 --- /dev/null +++ b/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/action/AmplitudeCreateOrUpdateUserAction.java @@ -0,0 +1,150 @@ +/* + * Copyright 2025 ByteChef + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.bytechef.component.amplitude.action; + +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.API_KEY; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.CARRIER; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.CITY; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.CONTENT_TYPE; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.CONTENT_TYPE_URLENCODED; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.COUNTRY; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.DEVICE_BRAND; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.DEVICE_ID; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.DMA; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.ID; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.IDENTIFICATION; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.LANGUAGE; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.OS_NAME; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.PLATFORM; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.USER_ID; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.USER_PROPERTIES_OBJECT; +import static com.bytechef.component.amplitude.util.AmplitudeUtils.getIdentification; +import static com.bytechef.component.definition.ComponentDsl.action; +import static com.bytechef.component.definition.ComponentDsl.option; +import static com.bytechef.component.definition.ComponentDsl.outputSchema; +import static com.bytechef.component.definition.ComponentDsl.string; +import static com.bytechef.component.definition.Context.Http.responseType; + +import com.bytechef.component.definition.ComponentDsl.ModifiableActionDefinition; +import com.bytechef.component.definition.Context; +import com.bytechef.component.definition.Context.Http.Body; +import com.bytechef.component.definition.Context.Http.BodyContentType; +import com.bytechef.component.definition.Context.Http.ResponseType; +import com.bytechef.component.definition.Parameters; +import com.bytechef.component.definition.TypeReference; +import java.util.HashMap; +import java.util.Map; + +/** + * @author Nikolina Spehar + */ +public class AmplitudeCreateOrUpdateUserAction { + + public static final ModifiableActionDefinition ACTION_DEFINITION = action("createOrUpdateUser") + .title("Create or Update User") + .description("Creates or updates user without sending an event.") + .properties( + string(ID) + .label("ID") + .description("Choose to create or update a user or a device.") + .options( + option("User ID", USER_ID), + option("Device ID", DEVICE_ID)) + .required(true), + string(DEVICE_ID) + .label("Device ID") + .description("A device specific identifier, such as the Identifier for Vendor (IDFV) on iOS.") + .displayCondition("%s == '%s'".formatted(ID, DEVICE_ID)) + .required(true), + string(USER_ID) + .label("User ID") + .description("Unique user ID specified by you. If you send a request with a user ID that's not in " + + "the Amplitude system, new user will be created (e.g. email address).") + .displayCondition("%s == '%s'".formatted(ID, USER_ID)) + .required(true), + USER_PROPERTIES_OBJECT, + string(PLATFORM) + .label("Platform") + .description("The platform that's sending the data.") + .required(false), + string(OS_NAME) + .label("Operating System Name") + .description("The mobile operating system or browser the user is on.") + .required(false), + string(DEVICE_BRAND) + .label("Device Brand") + .description("The device brand the user is on.") + .required(false), + string(CARRIER) + .label("Carrier") + .description("The carrier of the device the user is on.") + .required(false), + string(COUNTRY) + .label("Country") + .description("The country the user is in.") + .required(false), + string(CITY) + .label("City") + .description("The city the user is in.") + .required(false), + string(DMA) + .label("Designated Market Area ") + .description("The Designated Market Area of the user.") + .required(false), + string(LANGUAGE) + .label("Language") + .description("The language the user has set.") + .required(false)) + .output( + outputSchema( + string() + .description("Response message"))) + .perform(AmplitudeCreateOrUpdateUserAction::perform); + + private AmplitudeCreateOrUpdateUserAction() { + } + + public static String perform(Parameters inputParameters, Parameters connectionParameters, Context context) { + Map identification = getIdentification(inputParameters); + + Map body = new HashMap<>(); + + body.put(API_KEY, connectionParameters.getRequiredString(API_KEY)); + body.put(IDENTIFICATION, context.json(json -> json.write(identification))); + + checkIfNull(body, CARRIER, inputParameters.getString(CARRIER)); + checkIfNull(body, COUNTRY, inputParameters.getString(COUNTRY)); + checkIfNull(body, CITY, inputParameters.getString(CITY)); + checkIfNull(body, DEVICE_BRAND, inputParameters.getString(DEVICE_BRAND)); + checkIfNull(body, DMA, inputParameters.getString(DMA)); + checkIfNull(body, LANGUAGE, inputParameters.getString(LANGUAGE)); + checkIfNull(body, OS_NAME, inputParameters.getString(OS_NAME)); + + return context.http(http -> http.post("/identify")) + .configuration(responseType(ResponseType.TEXT)) + .header(CONTENT_TYPE, CONTENT_TYPE_URLENCODED) + .body(Body.of(body, BodyContentType.FORM_URL_ENCODED)) + .execute() + .getBody(new TypeReference<>() {}); + } + + private static void checkIfNull(Map body, String key, String value) { + if (value != null) { + body.put(key, value); + } + } +} diff --git a/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/connection/AmplitudeConnection.java b/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/connection/AmplitudeConnection.java new file mode 100644 index 00000000000..99c0ed0b76e --- /dev/null +++ b/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/connection/AmplitudeConnection.java @@ -0,0 +1,54 @@ +/* + * Copyright 2025 ByteChef + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.bytechef.component.amplitude.connection; + +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.API_KEY; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.REGION; +import static com.bytechef.component.definition.ComponentDsl.authorization; +import static com.bytechef.component.definition.ComponentDsl.connection; +import static com.bytechef.component.definition.ComponentDsl.option; +import static com.bytechef.component.definition.ComponentDsl.string; + +import com.bytechef.component.definition.Authorization.AuthorizationType; +import com.bytechef.component.definition.ComponentDsl.ModifiableConnectionDefinition; + +/** + * @author Nikolina Spehar + */ +public class AmplitudeConnection { + + public static final ModifiableConnectionDefinition CONNECTION_DEFINITION = connection() + .baseUri((connectionParameters, context) -> "https://%s.amplitude.com" + .formatted(connectionParameters.getRequiredString(REGION))) + .authorizations( + authorization(AuthorizationType.CUSTOM) + .properties( + string(API_KEY) + .label("API Key") + .description("The API key for the Amplitude project") + .required(true), + string(REGION) + .label("Region") + .description("Environment region you wish to access the API in.") + .options( + option("Standard", "api2"), + option("EU", "api.eu")) + .required(true))); + + private AmplitudeConnection() { + } +} diff --git a/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/constant/AmplitudeConstants.java b/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/constant/AmplitudeConstants.java new file mode 100644 index 00000000000..32a0f969256 --- /dev/null +++ b/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/constant/AmplitudeConstants.java @@ -0,0 +1,72 @@ +/* + * Copyright 2025 ByteChef + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.bytechef.component.amplitude.constant; + +import static com.bytechef.component.definition.ComponentDsl.array; +import static com.bytechef.component.definition.ComponentDsl.object; +import static com.bytechef.component.definition.ComponentDsl.string; + +import com.bytechef.component.definition.ComponentDsl.ModifiableArrayProperty; + +/** + * @author Nikolina Spehar + */ +public class AmplitudeConstants { + + public static final String API_KEY = "api_key"; + public static final String CARRIER = "carrier"; + public static final String CITY = "city"; + public static final String CONTENT_TYPE = "Content-Type"; + public static final String CONTENT_TYPE_URLENCODED = "application/x-www-urlencoded"; + public static final String COUNTRY = "country"; + public static final String DEVICE_BRAND = "device_brand"; + public static final String DEVICE_ID = "device_id"; + public static final String DMA = "dma"; + public static final String EVENT = "event"; + public static final String EVENT_TYPE = "event_type"; + public static final String ID = "id"; + public static final String IDENTIFICATION = "identification"; + public static final String IDENTIFIER = "identifier"; + public static final String KEY = "key"; + public static final String LANGUAGE = "language"; + public static final String OS_NAME = "os_name"; + public static final String PLATFORM = "platform"; + public static final String REGION = "region"; + public static final String USER_ID = "user_id"; + public static final String USER_PROPERTIES = "user_properties"; + public static final String VALUE = "value"; + + public static final ModifiableArrayProperty USER_PROPERTIES_OBJECT = array(USER_PROPERTIES) + .label("User Properties") + .description("A dictionary of attribution properties prefixed with brackets [YOUR COMPANY].") + .required(false) + .items( + object("property") + .label("Property") + .properties( + string(KEY) + .label("Key") + .description("The property key.") + .required(true), + string(VALUE) + .label("Value") + .description("The property value.") + .required(true))); + + private AmplitudeConstants() { + } +} diff --git a/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/util/AmplitudeUtils.java b/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/util/AmplitudeUtils.java new file mode 100644 index 00000000000..58f7f803e36 --- /dev/null +++ b/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/util/AmplitudeUtils.java @@ -0,0 +1,98 @@ +/* + * Copyright 2025 ByteChef + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.bytechef.component.amplitude.util; + +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.EVENT_TYPE; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.ID; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.IDENTIFIER; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.KEY; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.PLATFORM; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.USER_PROPERTIES; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.VALUE; +import static com.bytechef.component.definition.ComponentDsl.option; + +import com.bytechef.component.definition.Context; +import com.bytechef.component.definition.Option; +import com.bytechef.component.definition.Parameters; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Nikolina Spehar + */ +public class AmplitudeUtils { + + public static String getEventJson(Parameters inputParameters, Context context) { + Map identifier = inputParameters.getRequiredMap(IDENTIFIER, String.class); + + Map event = Map.of( + EVENT_TYPE, inputParameters.getRequiredString(EVENT_TYPE), + PLATFORM, inputParameters.getRequiredString(PLATFORM), + identifier.get(KEY), identifier.get(VALUE), + USER_PROPERTIES, getUserProperties(inputParameters)); + + return context.json(json -> json.write(event)); + } + + public static Map getIdentification(Parameters parameters) { + Map identification = new HashMap<>(); + + String id = parameters.getRequiredString(ID); + identification.put(id, parameters.getRequiredString(id)); + + identification.put(USER_PROPERTIES, getUserProperties(parameters)); + + return identification; + } + + public static List> getIdentifierKeyOptions( + Parameters inputParameters, Parameters connectionParameters, Map lookupDependsOnPaths, + String searchText, Context context) { + + List> options = new ArrayList<>(); + + if (inputParameters.getRequiredString(PLATFORM) + .equals("ios")) { + options.add(option("The Identifier for Advertiser", "idfa")); + options.add(option("The Identifier for Vendor", "idfv")); + } else { + options.add(option("The Google ADID", "adid")); + options.add(option("App Set ID", "android_app_set_id")); + } + + return options; + } + + public static Map getUserProperties(Parameters inputParameters) { + Map userProperties = new HashMap<>(); + + List userPropertiesList = inputParameters.getList(USER_PROPERTIES, Map.class); + + if (userPropertiesList != null) { + for (Map userProperty : userPropertiesList) { + userProperties.put(userProperty.get(KEY) + .toString(), + userProperty.get(VALUE) + .toString()); + } + } + + return userProperties; + } +} diff --git a/server/libs/modules/components/amplitude/src/main/resources/README.mdx b/server/libs/modules/components/amplitude/src/main/resources/README.mdx new file mode 100644 index 00000000000..e69de29bb2d diff --git a/server/libs/modules/components/amplitude/src/main/resources/assets/amplitude.svg b/server/libs/modules/components/amplitude/src/main/resources/assets/amplitude.svg new file mode 100644 index 00000000000..e67e1aa3006 --- /dev/null +++ b/server/libs/modules/components/amplitude/src/main/resources/assets/amplitude.svg @@ -0,0 +1,25 @@ + + + + + + + + + diff --git a/server/libs/modules/components/amplitude/src/test/java/com/bytechef/component/amplitude/AmplitudeComponentHandlerTest.java b/server/libs/modules/components/amplitude/src/test/java/com/bytechef/component/amplitude/AmplitudeComponentHandlerTest.java new file mode 100644 index 00000000000..40e83a86858 --- /dev/null +++ b/server/libs/modules/components/amplitude/src/test/java/com/bytechef/component/amplitude/AmplitudeComponentHandlerTest.java @@ -0,0 +1,31 @@ +/* + * Copyright 2025 ByteChef + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.bytechef.component.amplitude; + +import com.bytechef.test.jsonasssert.JsonFileAssert; +import org.junit.jupiter.api.Test; + +/** + * @author Nikolina Spehar + */ +class AmplitudeComponentHandlerTest { + + @Test + void testGetComponentDefinition() { + JsonFileAssert.assertEquals("definition/amplitude_v1.json", new AmplitudeComponentHandler().getDefinition()); + } +} diff --git a/server/libs/modules/components/amplitude/src/test/java/com/bytechef/component/amplitude/action/AmplitudeCreateAttributionEventActionTest.java b/server/libs/modules/components/amplitude/src/test/java/com/bytechef/component/amplitude/action/AmplitudeCreateAttributionEventActionTest.java new file mode 100644 index 00000000000..de9a72dcb2a --- /dev/null +++ b/server/libs/modules/components/amplitude/src/test/java/com/bytechef/component/amplitude/action/AmplitudeCreateAttributionEventActionTest.java @@ -0,0 +1,99 @@ +/* + * Copyright 2025 ByteChef + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.bytechef.component.amplitude.action; + +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.API_KEY; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.EVENT_TYPE; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.IDENTIFIER; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.KEY; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.PLATFORM; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.USER_PROPERTIES; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.VALUE; +import static com.bytechef.component.definition.Context.Http.responseType; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +import com.bytechef.component.amplitude.util.AmplitudeUtils; +import com.bytechef.component.definition.Context; +import com.bytechef.component.definition.Context.Http.Executor; +import com.bytechef.component.definition.Context.Http.Response; +import com.bytechef.component.definition.Parameters; +import com.bytechef.component.definition.TypeReference; +import com.bytechef.component.test.definition.MockParametersFactory; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.MockedStatic; + +/** + * @author Nikolina Spehar + */ +class AmplitudeCreateAttributionEventActionTest { + + private static final ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(Context.class); + private static final String jsonString = "jsonString"; + private static final Context mockedContext = mock(Context.class); + private static final Executor mockedExecutor = mock(Executor.class); + private static final Parameters mockedParameters = MockParametersFactory.create( + Map.of( + API_KEY, "api_key", EVENT_TYPE, "eventType", PLATFORM, "platform", + IDENTIFIER, Map.of(KEY, "identifierKey", VALUE, "identifierValue"), + USER_PROPERTIES, List.of(Map.of(KEY, "userPropertyKey", VALUE, "userPropertyValue")))); + private static final Response mockedResponse = mock(Response.class); + private static final ArgumentCaptor parametersArgumentCaptor = + ArgumentCaptor.forClass(Parameters.class); + private static final String responseString = "response"; + private static final ArgumentCaptor stringArgumentCaptor = ArgumentCaptor.forClass(String.class); + + @Test + void testPerform() { + when(mockedContext.http(any())) + .thenReturn(mockedExecutor); + when(mockedExecutor.configuration(responseType(any()))) + .thenReturn(mockedExecutor); + + try (MockedStatic amplitudeUtilsMockedStatic = mockStatic(AmplitudeUtils.class)) { + amplitudeUtilsMockedStatic + .when(() -> AmplitudeUtils.getEventJson( + parametersArgumentCaptor.capture(), contextArgumentCaptor.capture())) + .thenReturn(jsonString); + + when(mockedExecutor.queryParameters( + stringArgumentCaptor.capture(), stringArgumentCaptor.capture(), + stringArgumentCaptor.capture(), stringArgumentCaptor.capture())) + .thenReturn(mockedExecutor); + when(mockedExecutor.execute()) + .thenReturn(mockedResponse); + when(mockedResponse.getBody(any(TypeReference.class))) + .thenReturn(responseString); + + String response = + AmplitudeCreateAttributionEventAction.perform(mockedParameters, mockedParameters, mockedContext); + assertEquals(responseString, response); + + assertEquals(mockedParameters, parametersArgumentCaptor.getValue()); + assertEquals(mockedContext, contextArgumentCaptor.getValue()); + + List expectedQueryParameters = List.of(API_KEY, "api_key", "event", jsonString); + assertEquals(expectedQueryParameters, stringArgumentCaptor.getAllValues()); + } + } +} diff --git a/server/libs/modules/components/amplitude/src/test/java/com/bytechef/component/amplitude/action/AmplitudeCreateOrUpdateUserActionTest.java b/server/libs/modules/components/amplitude/src/test/java/com/bytechef/component/amplitude/action/AmplitudeCreateOrUpdateUserActionTest.java new file mode 100644 index 00000000000..5a94544ced7 --- /dev/null +++ b/server/libs/modules/components/amplitude/src/test/java/com/bytechef/component/amplitude/action/AmplitudeCreateOrUpdateUserActionTest.java @@ -0,0 +1,109 @@ +/* + * Copyright 2025 ByteChef + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.bytechef.component.amplitude.action; + +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.API_KEY; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.CONTENT_TYPE; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.CONTENT_TYPE_URLENCODED; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.ID; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.IDENTIFICATION; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.KEY; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.USER_PROPERTIES; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.VALUE; +import static com.bytechef.component.definition.Context.Http.responseType; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +import com.bytechef.component.amplitude.util.AmplitudeUtils; +import com.bytechef.component.definition.Context; +import com.bytechef.component.definition.Context.Http.Body; +import com.bytechef.component.definition.Context.Http.BodyContentType; +import com.bytechef.component.definition.Context.Http.Executor; +import com.bytechef.component.definition.Context.Http.Response; +import com.bytechef.component.definition.Parameters; +import com.bytechef.component.definition.TypeReference; +import com.bytechef.component.test.definition.MockParametersFactory; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.MockedStatic; + +/** + * @author Nikolina Spehar + */ +class AmplitudeCreateOrUpdateUserActionTest { + + private static final ArgumentCaptor bodyArgumentCaptor = ArgumentCaptor.forClass(Body.class); + private static final String identificationJson = "identificationJson"; + private static final Context mockedContext = mock(Context.class); + private static final Executor mockedExecutor = mock(Executor.class); + private static final Map mockedIdentification = Map.of(); + private static final Parameters mockedParameters = MockParametersFactory.create( + Map.of( + API_KEY, "api_key", ID, "id", USER_PROPERTIES, + List.of(Map.of(KEY, "userPropertyKey", VALUE, "userPropertyValue")))); + private static final Response mockedResponse = mock(Response.class); + private static final ArgumentCaptor parametersArgumentCaptor = + ArgumentCaptor.forClass(Parameters.class); + private static final String responseString = "response"; + private static final ArgumentCaptor stringArgumentCaptor = ArgumentCaptor.forClass(String.class); + + @Test + void testPerform() { + when(mockedContext.http(any())) + .thenReturn(mockedExecutor); + when(mockedExecutor.configuration(responseType(any()))) + .thenReturn(mockedExecutor); + when(mockedExecutor.header(stringArgumentCaptor.capture(), stringArgumentCaptor.capture())) + .thenReturn(mockedExecutor); + + try (MockedStatic amplitudeUtilsMockedStatic = mockStatic(AmplitudeUtils.class)) { + amplitudeUtilsMockedStatic + .when(() -> AmplitudeUtils.getIdentification( + parametersArgumentCaptor.capture())) + .thenReturn(mockedIdentification); + + when(mockedContext.json(any())) + .thenReturn(identificationJson); + + when(mockedExecutor.body(bodyArgumentCaptor.capture())) + .thenReturn(mockedExecutor); + when(mockedExecutor.execute()) + .thenReturn(mockedResponse); + when(mockedResponse.getBody(any(TypeReference.class))) + .thenReturn(responseString); + + String response = + AmplitudeCreateOrUpdateUserAction.perform(mockedParameters, mockedParameters, mockedContext); + assertEquals(responseString, response); + + assertEquals(mockedParameters, parametersArgumentCaptor.getValue()); + + List expectedHeader = List.of(CONTENT_TYPE, CONTENT_TYPE_URLENCODED); + assertEquals(expectedHeader, stringArgumentCaptor.getAllValues()); + + Body body = bodyArgumentCaptor.getValue(); + assertEquals(Map.of(API_KEY, "api_key", IDENTIFICATION, identificationJson), body.getContent()); + + assertEquals(BodyContentType.FORM_URL_ENCODED, body.getContentType()); + } + } +} diff --git a/server/libs/modules/components/amplitude/src/test/java/com/bytechef/component/amplitude/util/AmplitudeUtilsTest.java b/server/libs/modules/components/amplitude/src/test/java/com/bytechef/component/amplitude/util/AmplitudeUtilsTest.java new file mode 100644 index 00000000000..de0ae6256fc --- /dev/null +++ b/server/libs/modules/components/amplitude/src/test/java/com/bytechef/component/amplitude/util/AmplitudeUtilsTest.java @@ -0,0 +1,109 @@ +/* + * Copyright 2025 ByteChef + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.bytechef.component.amplitude.util; + +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.API_KEY; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.EVENT_TYPE; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.ID; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.IDENTIFIER; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.KEY; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.PLATFORM; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.USER_PROPERTIES; +import static com.bytechef.component.amplitude.constant.AmplitudeConstants.VALUE; +import static com.bytechef.component.definition.ComponentDsl.option; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.bytechef.component.definition.Context; +import com.bytechef.component.definition.Option; +import com.bytechef.component.definition.Parameters; +import com.bytechef.component.test.definition.MockParametersFactory; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; + +/** + * @author Nikolina Spehar + */ +class AmplitudeUtilsTest { + private static final Context mockedContext = mock(Context.class); + private static final Parameters mockedParameters = MockParametersFactory.create( + Map.of( + API_KEY, "api_key", EVENT_TYPE, "eventType", PLATFORM, "platform", ID, "id", + IDENTIFIER, Map.of(KEY, "identifierKey", VALUE, "identifierValue"), + USER_PROPERTIES, List.of(Map.of(KEY, "userPropertyKey", VALUE, "userPropertyValue")))); + private static final String responseString = "response"; + + @Test + void testGetEventJson() { + when(mockedContext.json(any())) + .thenReturn(responseString); + + String result = AmplitudeUtils.getEventJson(mockedParameters, mockedContext); + + assertEquals(responseString, result); + } + + @Test + void testGetIdentification() { + Map identification = AmplitudeUtils.getIdentification(mockedParameters); + + Map expectedIdentification = Map.of( + ID, ID, USER_PROPERTIES, Map.of("userPropertyKey", "userPropertyValue")); + + assertEquals(expectedIdentification, identification); + } + + @Test + void testGetIdentifierKeyOptionsIos() { + Parameters parameters = MockParametersFactory.create(Map.of(PLATFORM, "ios")); + + List> result = AmplitudeUtils.getIdentifierKeyOptions( + parameters, parameters, Map.of(), "", mockedContext); + + List> expected = List.of( + option("The Identifier for Advertiser", "idfa"), + option("The Identifier for Vendor", "idfv")); + + assertEquals(expected, result); + } + + @Test + void testGetIdentifierKeyOptionsAndroid() { + Parameters parameters = MockParametersFactory.create(Map.of(PLATFORM, "android")); + + List> result = AmplitudeUtils.getIdentifierKeyOptions( + parameters, parameters, Map.of(), "", mockedContext); + + List> expected = List.of( + option("The Google ADID", "adid"), + option("App Set ID", "android_app_set_id")); + + assertEquals(expected, result); + } + + @Test + void testGetUserProperties() { + Map result = AmplitudeUtils.getUserProperties(mockedParameters); + + Map expected = Map.of("userPropertyKey", "userPropertyValue"); + + assertEquals(expected, result); + } +} diff --git a/server/libs/modules/components/amplitude/src/test/resources/definition/amplitude_v1.json b/server/libs/modules/components/amplitude/src/test/resources/definition/amplitude_v1.json new file mode 100644 index 00000000000..f442a6878cc --- /dev/null +++ b/server/libs/modules/components/amplitude/src/test/resources/definition/amplitude_v1.json @@ -0,0 +1,1399 @@ +{ + "actions" : [ { + "batch" : null, + "deprecated" : null, + "description" : "Creates attribution event using Attribution API.", + "help" : null, + "metadata" : null, + "name" : "createAttributionEvent", + "outputDefinition" : { + "output" : null, + "outputResponse" : { + "outputSchema" : { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "Response message", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : null, + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : null, + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : null, + "type" : "STRING" + }, + "placeholder" : null, + "sampleOutput" : null + }, + "outputSchema" : { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "Response message", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : null, + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : null, + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : null, + "type" : "STRING" + }, + "sampleOutput" : null + }, + "perform" : { }, + "processErrorResponse" : null, + "properties" : [ { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "The event info. Prefix with brackets [YOUR COMPANY].", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Event Type", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "event_type", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : true, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "SELECT", + "defaultValue" : null, + "description" : "Platform which the event will occur on.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Platform", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "platform", + "options" : [ { + "description" : null, + "label" : "iOS", + "value" : "ios" + }, { + "description" : null, + "label" : "Android", + "value" : "android" + } ], + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : true, + "type" : "STRING" + }, { + "additionalProperties" : null, + "advancedOption" : null, + "controlType" : "OBJECT_BUILDER", + "defaultValue" : null, + "description" : "Identifier of the platform.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Identifier", + "metadata" : { }, + "multipleValues" : null, + "name" : "identifier", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "properties" : [ { + "advancedOption" : null, + "controlType" : "SELECT", + "defaultValue" : null, + "description" : "For iOS input the Identifier for Advertiser or the Identifier for Vendor.For Android input the Google ADID or App Set ID", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Identifier Key", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "key", + "options" : null, + "optionsDataSource" : { + "options" : { }, + "optionsLookupDependsOn" : [ "platform" ] + }, + "placeholder" : null, + "regex" : null, + "required" : true, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "Value of selected identifier.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Identifier Value", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "value", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : true, + "type" : "STRING" + } ], + "required" : true, + "type" : "OBJECT" + }, { + "advancedOption" : null, + "controlType" : "ARRAY_BUILDER", + "defaultValue" : null, + "description" : "A dictionary of attribution properties prefixed with brackets [YOUR COMPANY].", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "items" : [ { + "additionalProperties" : null, + "advancedOption" : null, + "controlType" : "OBJECT_BUILDER", + "defaultValue" : null, + "description" : null, + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Property", + "metadata" : { }, + "multipleValues" : null, + "name" : "property", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "properties" : [ { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "The property key.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Key", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "key", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : true, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "The property value.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Value", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "value", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : true, + "type" : "STRING" + } ], + "required" : null, + "type" : "OBJECT" + } ], + "label" : "User Properties", + "maxItems" : null, + "metadata" : { }, + "minItems" : null, + "multipleValues" : null, + "name" : "user_properties", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "required" : false, + "type" : "ARRAY" + } ], + "title" : "Create Attribution Event", + "workflowNodeDescription" : null + }, { + "batch" : null, + "deprecated" : null, + "description" : "Creates or updates user without sending an event.", + "help" : null, + "metadata" : null, + "name" : "createOrUpdateUser", + "outputDefinition" : { + "output" : null, + "outputResponse" : { + "outputSchema" : { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "Response message", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : null, + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : null, + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : null, + "type" : "STRING" + }, + "placeholder" : null, + "sampleOutput" : null + }, + "outputSchema" : { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "Response message", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : null, + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : null, + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : null, + "type" : "STRING" + }, + "sampleOutput" : null + }, + "perform" : { }, + "processErrorResponse" : null, + "properties" : [ { + "advancedOption" : null, + "controlType" : "SELECT", + "defaultValue" : null, + "description" : "Choose to create or update a user or a device.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "ID", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "id", + "options" : [ { + "description" : null, + "label" : "User ID", + "value" : "user_id" + }, { + "description" : null, + "label" : "Device ID", + "value" : "device_id" + } ], + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : true, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "A device specific identifier, such as the Identifier for Vendor (IDFV) on iOS.", + "displayCondition" : "id == 'device_id'", + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Device ID", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "device_id", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : true, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "Unique user ID specified by you. If you send a request with a user ID that's not in the Amplitude system, new user will be created (e.g. email address).", + "displayCondition" : "id == 'user_id'", + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "User ID", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "user_id", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : true, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "ARRAY_BUILDER", + "defaultValue" : null, + "description" : "A dictionary of attribution properties prefixed with brackets [YOUR COMPANY].", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "items" : [ { + "additionalProperties" : null, + "advancedOption" : null, + "controlType" : "OBJECT_BUILDER", + "defaultValue" : null, + "description" : null, + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Property", + "metadata" : { }, + "multipleValues" : null, + "name" : "property", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "properties" : [ { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "The property key.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Key", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "key", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : true, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "The property value.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Value", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "value", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : true, + "type" : "STRING" + } ], + "required" : null, + "type" : "OBJECT" + } ], + "label" : "User Properties", + "maxItems" : null, + "metadata" : { }, + "minItems" : null, + "multipleValues" : null, + "name" : "user_properties", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "required" : false, + "type" : "ARRAY" + }, { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "The platform that's sending the data.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Platform", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "platform", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : false, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "The mobile operating system or browser the user is on.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Operating System Name", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "os_name", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : false, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "The device brand the user is on.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Device Brand", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "device_brand", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : false, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "The carrier of the device the user is on.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Carrier", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "carrier", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : false, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "The country the user is in.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Country", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "country", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : false, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "The city the user is in.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "City", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "city", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : false, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "The Designated Market Area of the user.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Designated Market Area ", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "dma", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : false, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "The language the user has set.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Language", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "language", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : false, + "type" : "STRING" + } ], + "title" : "Create or Update User", + "workflowNodeDescription" : null + } ], + "clusterElements" : [ { + "description" : "Creates attribution event using Attribution API.", + "element" : { }, + "help" : null, + "name" : "createAttributionEvent", + "outputDefinition" : { + "output" : null, + "outputResponse" : { + "outputSchema" : { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "Response message", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : null, + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : null, + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : null, + "type" : "STRING" + }, + "placeholder" : null, + "sampleOutput" : null + }, + "outputSchema" : { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "Response message", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : null, + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : null, + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : null, + "type" : "STRING" + }, + "sampleOutput" : null + }, + "properties" : [ { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "The event info. Prefix with brackets [YOUR COMPANY].", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Event Type", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "event_type", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : true, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "SELECT", + "defaultValue" : null, + "description" : "Platform which the event will occur on.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Platform", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "platform", + "options" : [ { + "description" : null, + "label" : "iOS", + "value" : "ios" + }, { + "description" : null, + "label" : "Android", + "value" : "android" + } ], + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : true, + "type" : "STRING" + }, { + "additionalProperties" : null, + "advancedOption" : null, + "controlType" : "OBJECT_BUILDER", + "defaultValue" : null, + "description" : "Identifier of the platform.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Identifier", + "metadata" : { }, + "multipleValues" : null, + "name" : "identifier", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "properties" : [ { + "advancedOption" : null, + "controlType" : "SELECT", + "defaultValue" : null, + "description" : "For iOS input the Identifier for Advertiser or the Identifier for Vendor.For Android input the Google ADID or App Set ID", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Identifier Key", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "key", + "options" : null, + "optionsDataSource" : { + "options" : { }, + "optionsLookupDependsOn" : [ "platform" ] + }, + "placeholder" : null, + "regex" : null, + "required" : true, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "Value of selected identifier.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Identifier Value", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "value", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : true, + "type" : "STRING" + } ], + "required" : true, + "type" : "OBJECT" + }, { + "advancedOption" : null, + "controlType" : "ARRAY_BUILDER", + "defaultValue" : null, + "description" : "A dictionary of attribution properties prefixed with brackets [YOUR COMPANY].", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "items" : [ { + "additionalProperties" : null, + "advancedOption" : null, + "controlType" : "OBJECT_BUILDER", + "defaultValue" : null, + "description" : null, + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Property", + "metadata" : { }, + "multipleValues" : null, + "name" : "property", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "properties" : [ { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "The property key.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Key", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "key", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : true, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "The property value.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Value", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "value", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : true, + "type" : "STRING" + } ], + "required" : null, + "type" : "OBJECT" + } ], + "label" : "User Properties", + "maxItems" : null, + "metadata" : { }, + "minItems" : null, + "multipleValues" : null, + "name" : "user_properties", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "required" : false, + "type" : "ARRAY" + } ], + "title" : "Create Attribution Event", + "type" : { + "key" : "tools", + "label" : "Tools", + "multipleElements" : true, + "name" : "TOOLS", + "required" : false + } + }, { + "description" : "Creates or updates user without sending an event.", + "element" : { }, + "help" : null, + "name" : "createOrUpdateUser", + "outputDefinition" : { + "output" : null, + "outputResponse" : { + "outputSchema" : { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "Response message", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : null, + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : null, + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : null, + "type" : "STRING" + }, + "placeholder" : null, + "sampleOutput" : null + }, + "outputSchema" : { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "Response message", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : null, + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : null, + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : null, + "type" : "STRING" + }, + "sampleOutput" : null + }, + "properties" : [ { + "advancedOption" : null, + "controlType" : "SELECT", + "defaultValue" : null, + "description" : "Choose to create or update a user or a device.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "ID", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "id", + "options" : [ { + "description" : null, + "label" : "User ID", + "value" : "user_id" + }, { + "description" : null, + "label" : "Device ID", + "value" : "device_id" + } ], + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : true, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "A device specific identifier, such as the Identifier for Vendor (IDFV) on iOS.", + "displayCondition" : "id == 'device_id'", + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Device ID", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "device_id", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : true, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "Unique user ID specified by you. If you send a request with a user ID that's not in the Amplitude system, new user will be created (e.g. email address).", + "displayCondition" : "id == 'user_id'", + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "User ID", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "user_id", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : true, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "ARRAY_BUILDER", + "defaultValue" : null, + "description" : "A dictionary of attribution properties prefixed with brackets [YOUR COMPANY].", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "items" : [ { + "additionalProperties" : null, + "advancedOption" : null, + "controlType" : "OBJECT_BUILDER", + "defaultValue" : null, + "description" : null, + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Property", + "metadata" : { }, + "multipleValues" : null, + "name" : "property", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "properties" : [ { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "The property key.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Key", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "key", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : true, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "The property value.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Value", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "value", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : true, + "type" : "STRING" + } ], + "required" : null, + "type" : "OBJECT" + } ], + "label" : "User Properties", + "maxItems" : null, + "metadata" : { }, + "minItems" : null, + "multipleValues" : null, + "name" : "user_properties", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "required" : false, + "type" : "ARRAY" + }, { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "The platform that's sending the data.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Platform", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "platform", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : false, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "The mobile operating system or browser the user is on.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Operating System Name", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "os_name", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : false, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "The device brand the user is on.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Device Brand", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "device_brand", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : false, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "The carrier of the device the user is on.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Carrier", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "carrier", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : false, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "The country the user is in.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Country", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "country", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : false, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "The city the user is in.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "City", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "city", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : false, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "The Designated Market Area of the user.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Designated Market Area ", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "dma", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : false, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "The language the user has set.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Language", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "language", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : false, + "type" : "STRING" + } ], + "title" : "Create or Update User", + "type" : { + "key" : "tools", + "label" : "Tools", + "multipleElements" : true, + "name" : "TOOLS", + "required" : false + } + } ], + "componentCategories" : [ { + "label" : "Analytics", + "name" : "analytics" + } ], + "connection" : { + "authorizationRequired" : null, + "authorizations" : [ { + "acquire" : null, + "apply" : null, + "authorizationCallback" : null, + "authorizationUrl" : null, + "clientId" : null, + "clientSecret" : null, + "description" : null, + "detectOn" : null, + "name" : "custom", + "oauth2AuthorizationExtraQueryParameters" : null, + "pkce" : null, + "properties" : [ { + "advancedOption" : null, + "controlType" : "TEXT", + "defaultValue" : null, + "description" : "The API key for the Amplitude project", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "API Key", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "api_key", + "options" : null, + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : true, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "SELECT", + "defaultValue" : null, + "description" : "Environment region you wish to access the API in.", + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Region", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "region", + "options" : [ { + "description" : null, + "label" : "Standard", + "value" : "api2" + }, { + "description" : null, + "label" : "EU", + "value" : "api.eu" + } ], + "optionsDataSource" : null, + "placeholder" : null, + "regex" : null, + "required" : true, + "type" : "STRING" + } ], + "refresh" : null, + "refreshOn" : null, + "refreshToken" : null, + "refreshUrl" : null, + "scopes" : null, + "title" : null, + "tokenUrl" : null, + "type" : "CUSTOM" + } ], + "baseUri" : { }, + "properties" : null, + "test" : null, + "version" : 1 + }, + "customAction" : null, + "customActionHelp" : null, + "description" : "Build better products by turning your user data into meaningful insights, using Amplitude's digital analytics platform and experimentation tools.", + "icon" : "path:assets/amplitude.svg", + "metadata" : null, + "name" : "amplitude", + "resources" : null, + "tags" : null, + "title" : "Amplitude", + "triggers" : null, + "unifiedApi" : null, + "version" : 1 +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 049e1c1c3bb..9909c6b202a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -212,6 +212,7 @@ include("server:libs:modules:components:ai:vectorstore:typesense") include("server:libs:modules:components:ai:vectorstore:weaviate") include("server:libs:modules:components:airtable") include("server:libs:modules:components:aitable") +include("server:libs:modules:components:amplitude") include("server:libs:modules:components:apify") include("server:libs:modules:components:apollo") include("server:libs:modules:components:app-event") From 667a2b8e4616051cccdcd82c3a21c3944f979e13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Monika=20Ku=C5=A1ter?= Date: Thu, 17 Jul 2025 16:37:43 +0200 Subject: [PATCH 2/3] 2785 - SF --- ...AmplitudeCreateAttributionEventAction.java | 9 ++-- .../AmplitudeCreateOrUpdateUserAction.java | 8 ++-- .../amplitude/util/AmplitudeUtils.java | 18 ++++--- ...itudeCreateAttributionEventActionTest.java | 40 +++++++--------- ...AmplitudeCreateOrUpdateUserActionTest.java | 48 +++++++++---------- .../amplitude/util/AmplitudeUtilsTest.java | 8 ++-- 6 files changed, 61 insertions(+), 70 deletions(-) diff --git a/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/action/AmplitudeCreateAttributionEventAction.java b/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/action/AmplitudeCreateAttributionEventAction.java index 5f7ce5c5735..06da76b6379 100644 --- a/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/action/AmplitudeCreateAttributionEventAction.java +++ b/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/action/AmplitudeCreateAttributionEventAction.java @@ -38,7 +38,6 @@ import com.bytechef.component.definition.Context.Http.ResponseType; import com.bytechef.component.definition.OptionsDataSource.ActionOptionsFunction; import com.bytechef.component.definition.Parameters; -import com.bytechef.component.definition.TypeReference; /** * @author Nikolina Spehar @@ -67,8 +66,9 @@ public class AmplitudeCreateAttributionEventAction { .properties( string(KEY) .label("Identifier Key") - .description("For iOS input the Identifier for Advertiser or the Identifier for Vendor." + - "For Android input the Google ADID or App Set ID") + .description( + "For iOS input the Identifier for Advertiser or the Identifier for Vendor.For Android " + + "input the Google ADID or App Set ID.") .options((ActionOptionsFunction) AmplitudeUtils::getIdentifierKeyOptions) .optionsLookupDependsOn(PLATFORM) .required(true), @@ -87,13 +87,12 @@ private AmplitudeCreateAttributionEventAction() { } public static String perform(Parameters inputParameters, Parameters connectionParameters, Context context) { - return context.http(http -> http.post("/attribution")) .configuration(responseType(ResponseType.TEXT)) .queryParameters( API_KEY, connectionParameters.getRequiredString(API_KEY), EVENT, getEventJson(inputParameters, context)) .execute() - .getBody(new TypeReference<>() {}); + .getBody(String.class); } } diff --git a/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/action/AmplitudeCreateOrUpdateUserAction.java b/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/action/AmplitudeCreateOrUpdateUserAction.java index e2879205c43..e21a7dd3875 100644 --- a/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/action/AmplitudeCreateOrUpdateUserAction.java +++ b/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/action/AmplitudeCreateOrUpdateUserAction.java @@ -45,7 +45,6 @@ import com.bytechef.component.definition.Context.Http.BodyContentType; import com.bytechef.component.definition.Context.Http.ResponseType; import com.bytechef.component.definition.Parameters; -import com.bytechef.component.definition.TypeReference; import java.util.HashMap; import java.util.Map; @@ -72,8 +71,9 @@ public class AmplitudeCreateOrUpdateUserAction { .required(true), string(USER_ID) .label("User ID") - .description("Unique user ID specified by you. If you send a request with a user ID that's not in " + - "the Amplitude system, new user will be created (e.g. email address).") + .description( + "Unique user ID specified by you. If you send a request with a user ID that's not in the " + + "Amplitude system, new user will be created (e.g. email address).") .displayCondition("%s == '%s'".formatted(ID, USER_ID)) .required(true), USER_PROPERTIES_OBJECT, @@ -139,7 +139,7 @@ public static String perform(Parameters inputParameters, Parameters connectionPa .header(CONTENT_TYPE, CONTENT_TYPE_URLENCODED) .body(Body.of(body, BodyContentType.FORM_URL_ENCODED)) .execute() - .getBody(new TypeReference<>() {}); + .getBody(String.class); } private static void checkIfNull(Map body, String key, String value) { diff --git a/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/util/AmplitudeUtils.java b/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/util/AmplitudeUtils.java index 58f7f803e36..e6608f0e9b4 100644 --- a/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/util/AmplitudeUtils.java +++ b/server/libs/modules/components/amplitude/src/main/java/com/bytechef/component/amplitude/util/AmplitudeUtils.java @@ -28,6 +28,7 @@ import com.bytechef.component.definition.Context; import com.bytechef.component.definition.Option; import com.bytechef.component.definition.Parameters; +import com.bytechef.component.definition.TypeReference; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -67,8 +68,9 @@ public static List> getIdentifierKeyOptions( List> options = new ArrayList<>(); - if (inputParameters.getRequiredString(PLATFORM) - .equals("ios")) { + String platform = inputParameters.getRequiredString(PLATFORM); + + if (platform.equals("ios")) { options.add(option("The Identifier for Advertiser", "idfa")); options.add(option("The Identifier for Vendor", "idfv")); } else { @@ -82,15 +84,11 @@ public static List> getIdentifierKeyOptions( public static Map getUserProperties(Parameters inputParameters) { Map userProperties = new HashMap<>(); - List userPropertiesList = inputParameters.getList(USER_PROPERTIES, Map.class); + List> userPropertiesList = inputParameters.getList( + USER_PROPERTIES, new TypeReference<>() {}, List.of()); - if (userPropertiesList != null) { - for (Map userProperty : userPropertiesList) { - userProperties.put(userProperty.get(KEY) - .toString(), - userProperty.get(VALUE) - .toString()); - } + for (Map userProperty : userPropertiesList) { + userProperties.put(userProperty.get(KEY), userProperty.get(VALUE)); } return userProperties; diff --git a/server/libs/modules/components/amplitude/src/test/java/com/bytechef/component/amplitude/action/AmplitudeCreateAttributionEventActionTest.java b/server/libs/modules/components/amplitude/src/test/java/com/bytechef/component/amplitude/action/AmplitudeCreateAttributionEventActionTest.java index de9a72dcb2a..866eba908e7 100644 --- a/server/libs/modules/components/amplitude/src/test/java/com/bytechef/component/amplitude/action/AmplitudeCreateAttributionEventActionTest.java +++ b/server/libs/modules/components/amplitude/src/test/java/com/bytechef/component/amplitude/action/AmplitudeCreateAttributionEventActionTest.java @@ -35,7 +35,6 @@ import com.bytechef.component.definition.Context.Http.Executor; import com.bytechef.component.definition.Context.Http.Response; import com.bytechef.component.definition.Parameters; -import com.bytechef.component.definition.TypeReference; import com.bytechef.component.test.definition.MockParametersFactory; import java.util.List; import java.util.Map; @@ -48,27 +47,22 @@ */ class AmplitudeCreateAttributionEventActionTest { - private static final ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(Context.class); - private static final String jsonString = "jsonString"; - private static final Context mockedContext = mock(Context.class); - private static final Executor mockedExecutor = mock(Executor.class); - private static final Parameters mockedParameters = MockParametersFactory.create( + private final ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(Context.class); + private final Context mockedContext = mock(Context.class); + private final Executor mockedExecutor = mock(Executor.class); + private final Parameters mockedParameters = MockParametersFactory.create( Map.of( API_KEY, "api_key", EVENT_TYPE, "eventType", PLATFORM, "platform", IDENTIFIER, Map.of(KEY, "identifierKey", VALUE, "identifierValue"), USER_PROPERTIES, List.of(Map.of(KEY, "userPropertyKey", VALUE, "userPropertyValue")))); - private static final Response mockedResponse = mock(Response.class); - private static final ArgumentCaptor parametersArgumentCaptor = - ArgumentCaptor.forClass(Parameters.class); - private static final String responseString = "response"; - private static final ArgumentCaptor stringArgumentCaptor = ArgumentCaptor.forClass(String.class); + private final Response mockedResponse = mock(Response.class); + private final ArgumentCaptor parametersArgumentCaptor = ArgumentCaptor.forClass(Parameters.class); + private final ArgumentCaptor stringArgumentCaptor = ArgumentCaptor.forClass(String.class); @Test void testPerform() { - when(mockedContext.http(any())) - .thenReturn(mockedExecutor); - when(mockedExecutor.configuration(responseType(any()))) - .thenReturn(mockedExecutor); + String jsonString = "jsonString"; + String responseString = "response"; try (MockedStatic amplitudeUtilsMockedStatic = mockStatic(AmplitudeUtils.class)) { amplitudeUtilsMockedStatic @@ -76,24 +70,26 @@ void testPerform() { parametersArgumentCaptor.capture(), contextArgumentCaptor.capture())) .thenReturn(jsonString); + when(mockedContext.http(any())) + .thenReturn(mockedExecutor); + when(mockedExecutor.configuration(responseType(any()))) + .thenReturn(mockedExecutor); when(mockedExecutor.queryParameters( stringArgumentCaptor.capture(), stringArgumentCaptor.capture(), stringArgumentCaptor.capture(), stringArgumentCaptor.capture())) .thenReturn(mockedExecutor); when(mockedExecutor.execute()) .thenReturn(mockedResponse); - when(mockedResponse.getBody(any(TypeReference.class))) + when(mockedResponse.getBody(String.class)) .thenReturn(responseString); - String response = - AmplitudeCreateAttributionEventAction.perform(mockedParameters, mockedParameters, mockedContext); - assertEquals(responseString, response); + String response = AmplitudeCreateAttributionEventAction.perform( + mockedParameters, mockedParameters, mockedContext); + assertEquals(responseString, response); assertEquals(mockedParameters, parametersArgumentCaptor.getValue()); assertEquals(mockedContext, contextArgumentCaptor.getValue()); - - List expectedQueryParameters = List.of(API_KEY, "api_key", "event", jsonString); - assertEquals(expectedQueryParameters, stringArgumentCaptor.getAllValues()); + assertEquals(List.of(API_KEY, "api_key", "event", jsonString), stringArgumentCaptor.getAllValues()); } } } diff --git a/server/libs/modules/components/amplitude/src/test/java/com/bytechef/component/amplitude/action/AmplitudeCreateOrUpdateUserActionTest.java b/server/libs/modules/components/amplitude/src/test/java/com/bytechef/component/amplitude/action/AmplitudeCreateOrUpdateUserActionTest.java index 5a94544ced7..08232b3f7e3 100644 --- a/server/libs/modules/components/amplitude/src/test/java/com/bytechef/component/amplitude/action/AmplitudeCreateOrUpdateUserActionTest.java +++ b/server/libs/modules/components/amplitude/src/test/java/com/bytechef/component/amplitude/action/AmplitudeCreateOrUpdateUserActionTest.java @@ -38,7 +38,6 @@ import com.bytechef.component.definition.Context.Http.Executor; import com.bytechef.component.definition.Context.Http.Response; import com.bytechef.component.definition.Parameters; -import com.bytechef.component.definition.TypeReference; import com.bytechef.component.test.definition.MockParametersFactory; import java.util.List; import java.util.Map; @@ -51,29 +50,22 @@ */ class AmplitudeCreateOrUpdateUserActionTest { - private static final ArgumentCaptor bodyArgumentCaptor = ArgumentCaptor.forClass(Body.class); - private static final String identificationJson = "identificationJson"; - private static final Context mockedContext = mock(Context.class); - private static final Executor mockedExecutor = mock(Executor.class); - private static final Map mockedIdentification = Map.of(); - private static final Parameters mockedParameters = MockParametersFactory.create( + private final ArgumentCaptor bodyArgumentCaptor = ArgumentCaptor.forClass(Body.class); + private final Context mockedContext = mock(Context.class); + private final Executor mockedExecutor = mock(Executor.class); + private final Map mockedIdentification = Map.of(); + private final Parameters mockedParameters = MockParametersFactory.create( Map.of( API_KEY, "api_key", ID, "id", USER_PROPERTIES, List.of(Map.of(KEY, "userPropertyKey", VALUE, "userPropertyValue")))); - private static final Response mockedResponse = mock(Response.class); - private static final ArgumentCaptor parametersArgumentCaptor = - ArgumentCaptor.forClass(Parameters.class); - private static final String responseString = "response"; - private static final ArgumentCaptor stringArgumentCaptor = ArgumentCaptor.forClass(String.class); + private final Response mockedResponse = mock(Response.class); + private final ArgumentCaptor parametersArgumentCaptor = ArgumentCaptor.forClass(Parameters.class); + private final ArgumentCaptor stringArgumentCaptor = ArgumentCaptor.forClass(String.class); @Test void testPerform() { - when(mockedContext.http(any())) - .thenReturn(mockedExecutor); - when(mockedExecutor.configuration(responseType(any()))) - .thenReturn(mockedExecutor); - when(mockedExecutor.header(stringArgumentCaptor.capture(), stringArgumentCaptor.capture())) - .thenReturn(mockedExecutor); + String identificationJson = "identificationJson"; + String responseString = "response"; try (MockedStatic amplitudeUtilsMockedStatic = mockStatic(AmplitudeUtils.class)) { amplitudeUtilsMockedStatic @@ -84,25 +76,29 @@ void testPerform() { when(mockedContext.json(any())) .thenReturn(identificationJson); + when(mockedContext.http(any())) + .thenReturn(mockedExecutor); + when(mockedExecutor.configuration(responseType(any()))) + .thenReturn(mockedExecutor); + when(mockedExecutor.header(stringArgumentCaptor.capture(), stringArgumentCaptor.capture())) + .thenReturn(mockedExecutor); when(mockedExecutor.body(bodyArgumentCaptor.capture())) .thenReturn(mockedExecutor); when(mockedExecutor.execute()) .thenReturn(mockedResponse); - when(mockedResponse.getBody(any(TypeReference.class))) + when(mockedResponse.getBody(String.class)) .thenReturn(responseString); - String response = - AmplitudeCreateOrUpdateUserAction.perform(mockedParameters, mockedParameters, mockedContext); - assertEquals(responseString, response); + String response = AmplitudeCreateOrUpdateUserAction.perform( + mockedParameters, mockedParameters, mockedContext); + assertEquals(responseString, response); assertEquals(mockedParameters, parametersArgumentCaptor.getValue()); - - List expectedHeader = List.of(CONTENT_TYPE, CONTENT_TYPE_URLENCODED); - assertEquals(expectedHeader, stringArgumentCaptor.getAllValues()); + assertEquals(List.of(CONTENT_TYPE, CONTENT_TYPE_URLENCODED), stringArgumentCaptor.getAllValues()); Body body = bodyArgumentCaptor.getValue(); - assertEquals(Map.of(API_KEY, "api_key", IDENTIFICATION, identificationJson), body.getContent()); + assertEquals(Map.of(API_KEY, "api_key", IDENTIFICATION, identificationJson), body.getContent()); assertEquals(BodyContentType.FORM_URL_ENCODED, body.getContentType()); } } diff --git a/server/libs/modules/components/amplitude/src/test/java/com/bytechef/component/amplitude/util/AmplitudeUtilsTest.java b/server/libs/modules/components/amplitude/src/test/java/com/bytechef/component/amplitude/util/AmplitudeUtilsTest.java index de0ae6256fc..7242af0d6ed 100644 --- a/server/libs/modules/components/amplitude/src/test/java/com/bytechef/component/amplitude/util/AmplitudeUtilsTest.java +++ b/server/libs/modules/components/amplitude/src/test/java/com/bytechef/component/amplitude/util/AmplitudeUtilsTest.java @@ -42,16 +42,18 @@ * @author Nikolina Spehar */ class AmplitudeUtilsTest { - private static final Context mockedContext = mock(Context.class); - private static final Parameters mockedParameters = MockParametersFactory.create( + + private final Context mockedContext = mock(Context.class); + private final Parameters mockedParameters = MockParametersFactory.create( Map.of( API_KEY, "api_key", EVENT_TYPE, "eventType", PLATFORM, "platform", ID, "id", IDENTIFIER, Map.of(KEY, "identifierKey", VALUE, "identifierValue"), USER_PROPERTIES, List.of(Map.of(KEY, "userPropertyKey", VALUE, "userPropertyValue")))); - private static final String responseString = "response"; @Test void testGetEventJson() { + String responseString = "response"; + when(mockedContext.json(any())) .thenReturn(responseString); From dae4886111b7a8b796b3ed2879f59c5cb73404e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Monika=20Ku=C5=A1ter?= Date: Thu, 17 Jul 2025 16:46:35 +0200 Subject: [PATCH 3/3] 2785 - generated --- .../docs/reference/components/amplitude.mdx | 38 +++++++++---------- .../resources/definition/amplitude_v1.json | 4 +- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/docs/content/docs/reference/components/amplitude.mdx b/docs/content/docs/reference/components/amplitude.mdx index 7e81cbe528c..fcc0a2105e2 100644 --- a/docs/content/docs/reference/components/amplitude.mdx +++ b/docs/content/docs/reference/components/amplitude.mdx @@ -24,8 +24,8 @@ Version: 1 | Name | Label | Type | Description | Required | |:---------------:|:--------------:|:------------:|:-------------------:|:--------:| -| api_key | API Key | STRING | `The API key for the Amplitude project` | true | -| region | Region | STRING
Options api2, api.eu
| `Environment region you wish to access the API in.` | true | +| api_key | API Key | STRING | The API key for the Amplitude project | true | +| region | Region | STRING
Options api2, api.eu
| Environment region you wish to access the API in. | true | @@ -34,7 +34,6 @@ Version: 1
- ## Actions @@ -47,10 +46,10 @@ Name: createAttributionEvent | Name | Label | Type | Description | Required | |:---------------:|:--------------:|:------------:|:-------------------:|:--------:| -| event_type | Event Type | STRING | `The event info. Prefix with brackets [YOUR COMPANY].` | true | -| platform | Platform | STRING
Options ios, android
| `Platform which the event will occur on.` | true | -| identifier | Identifier | OBJECT
Properties {STRING\(key), STRING\(value)}
| `Identifier of the platform.` | true | -| user_properties | User Properties | ARRAY
Items [{STRING\(key), STRING\(value)}\($property)]
| `A dictionary of attribution properties prefixed with brackets [YOUR COMPANY].` | false | +| event_type | Event Type | STRING | The event info. Prefix with brackets [YOUR COMPANY]. | true | +| platform | Platform | STRING
Options ios, android
| Platform which the event will occur on. | true | +| identifier | Identifier | OBJECT
Properties {STRING\(key), STRING\(value)}
| Identifier of the platform. | true | +| user_properties | User Properties | ARRAY
Items [{STRING\(key), STRING\(value)}\($property)]
| A dictionary of attribution properties prefixed with brackets [YOUR COMPANY]. | false | #### Example JSON Structure ```json @@ -95,18 +94,18 @@ Name: createOrUpdateUser | Name | Label | Type | Description | Required | |:---------------:|:--------------:|:------------:|:-------------------:|:--------:| -| id | ID | STRING
Options user_id, device_id
| `Choose to create or update a user or a device.` | true | -| device_id | Device ID | STRING | `A device specific identifier, such as the Identifier for Vendor (IDFV) on iOS.` | true | -| user_id | User ID | STRING | `Unique user ID specified by you. If you send a request with a user ID that's not in the Amplitude system, new user will be created (e.g. email address).` | true | -| user_properties | User Properties | ARRAY
Items [{STRING\(key), STRING\(value)}\($property)]
| `A dictionary of attribution properties prefixed with brackets [YOUR COMPANY].` | false | -| platform | Platform | STRING | `The platform that's sending the data.` | false | -| os_name | Operating System Name | STRING | `The mobile operating system or browser the user is on.` | false | -| device_brand | Device Brand | STRING | `The device brand the user is on.` | false | -| carrier | Carrier | STRING | `The carrier of the device the user is on.` | false | -| country | Country | STRING | `The country the user is in.` | false | -| city | City | STRING | `The city the user is in.` | false | -| dma | Designated Market Area | STRING | `The Designated Market Area of the user.` | false | -| language | Language | STRING | `The language the user has set.` | false | +| id | ID | STRING
Options user_id, device_id
| Choose to create or update a user or a device. | true | +| device_id | Device ID | STRING | A device specific identifier, such as the Identifier for Vendor (IDFV) on iOS. | true | +| user_id | User ID | STRING | Unique user ID specified by you. If you send a request with a user ID that's not in the Amplitude system, new user will be created (e.g. email address). | true | +| user_properties | User Properties | ARRAY
Items [{STRING\(key), STRING\(value)}\($property)]
| A dictionary of attribution properties prefixed with brackets [YOUR COMPANY]. | false | +| platform | Platform | STRING | The platform that's sending the data. | false | +| os_name | Operating System Name | STRING | The mobile operating system or browser the user is on. | false | +| device_brand | Device Brand | STRING | The device brand the user is on. | false | +| carrier | Carrier | STRING | The carrier of the device the user is on. | false | +| country | Country | STRING | The country the user is in. | false | +| city | City | STRING | The city the user is in. | false | +| dma | Designated Market Area | STRING | The Designated Market Area of the user. | false | +| language | Language | STRING | The language the user has set. | false | #### Example JSON Structure ```json @@ -152,5 +151,4 @@ Type: STRING
# Additional instructions -
diff --git a/server/libs/modules/components/amplitude/src/test/resources/definition/amplitude_v1.json b/server/libs/modules/components/amplitude/src/test/resources/definition/amplitude_v1.json index f442a6878cc..2c3171b8930 100644 --- a/server/libs/modules/components/amplitude/src/test/resources/definition/amplitude_v1.json +++ b/server/libs/modules/components/amplitude/src/test/resources/definition/amplitude_v1.json @@ -131,7 +131,7 @@ "advancedOption" : null, "controlType" : "SELECT", "defaultValue" : null, - "description" : "For iOS input the Identifier for Advertiser or the Identifier for Vendor.For Android input the Google ADID or App Set ID", + "description" : "For iOS input the Identifier for Advertiser or the Identifier for Vendor.For Android input the Google ADID or App Set ID.", "displayCondition" : null, "exampleValue" : null, "expressionEnabled" : null, @@ -776,7 +776,7 @@ "advancedOption" : null, "controlType" : "SELECT", "defaultValue" : null, - "description" : "For iOS input the Identifier for Advertiser or the Identifier for Vendor.For Android input the Google ADID or App Set ID", + "description" : "For iOS input the Identifier for Advertiser or the Identifier for Vendor.For Android input the Google ADID or App Set ID.", "displayCondition" : null, "exampleValue" : null, "expressionEnabled" : null,