diff --git a/cli/commands/component/init/openapi/src/main/java/com/bytechef/cli/command/component/init/openapi/ComponentInitOpenApiGenerator.java b/cli/commands/component/init/openapi/src/main/java/com/bytechef/cli/command/component/init/openapi/ComponentInitOpenApiGenerator.java index df712e434cd..5e345081e0a 100644 --- a/cli/commands/component/init/openapi/src/main/java/com/bytechef/cli/command/component/init/openapi/ComponentInitOpenApiGenerator.java +++ b/cli/commands/component/init/openapi/src/main/java/com/bytechef/cli/command/component/init/openapi/ComponentInitOpenApiGenerator.java @@ -1492,7 +1492,7 @@ private PropertiesEntry getRequestBodyPropertiesItem(Operation operation, OpenAP bodyContentType = switch (mimeType) { case "application/json" -> "JSON"; case "application/xml" -> "XML"; - case "application/x-www-form-urlencoded" -> "FORM_URLENCODED"; + case "application/x-www-form-urlencoded" -> "FORM_URL_ENCODED"; case "application/octet-stream" -> "BINARY"; case "multipart/form-data" -> "FORM_DATA"; default -> "RAW"; diff --git a/docs/src/content/docs/reference/components/stripe.md b/docs/src/content/docs/reference/components/stripe.md new file mode 100644 index 00000000000..8788d9b7ea2 --- /dev/null +++ b/docs/src/content/docs/reference/components/stripe.md @@ -0,0 +1,177 @@ +--- +title: "Stripe" +description: "Stripe is a payment processing platform that allows businesses to accept online payments and manage transactions securely." +--- +## Reference +
+ +Stripe is a payment processing platform that allows businesses to accept online payments and manage transactions securely. + + +Categories: [payment-processing] + + +Version: 1 + +
+ + + +## Connections + +Version: 1 + + +### Bearer Token + +#### Properties + +| Name | Type | Control Type | Description | +|:--------------:|:------------:|:--------------------:|:-------------------:| +| Token | STRING | TEXT | | + + + + + +
+ + + +## Triggers + + +### New Customer +Triggers when a new customer is created. + +#### Type: DYNAMIC_WEBHOOK +#### Properties + +| Name | Type | Control Type | Description | +|:--------------:|:------------:|:--------------------:|:-------------------:| +null + + +### Output + + + +Type: OBJECT + + +#### Properties + +| Type | Control Type | +|:------------:|:--------------------:| +| STRING | TEXT | +| STRING | TEXT | +| STRING | TEXT | +| STRING | TEXT | +| STRING | TEXT | +| STRING | TEXT | +| {STRING\(city), STRING\(country), STRING\(line1), STRING\(line2), STRING\(postal_code), STRING\(state)} | OBJECT_BUILDER | + + + + + + + +### New Invoice +Triggers on a new invoice. + +#### Type: DYNAMIC_WEBHOOK +#### Properties + +| Name | Type | Control Type | Description | +|:--------------:|:------------:|:--------------------:|:-------------------:| +null + + +### Output + + + +Type: OBJECT + + +#### Properties + +| Type | Control Type | +|:------------:|:--------------------:| +| STRING | TEXT | +| STRING | TEXT | +| STRING | TEXT | +| STRING | TEXT | +| STRING | TEXT | +| STRING | TEXT | + + + + + + + +
+ + + +## Actions + + +### Create Customer +Creates a new customer. + +#### Properties + +| Name | Type | Control Type | Description | +|:--------------:|:------------:|:--------------------:|:-------------------:| +| Customer | {STRING\(email), STRING\(name), STRING\(description), STRING\(phone), {STRING\(city), STRING\(country), STRING\(line1), STRING\(line2), STRING\(postal_code), STRING\(state)}\(address)} | OBJECT_BUILDER | | + + +### Output + + + +Type: OBJECT + + +#### Properties + +| Type | Control Type | +|:------------:|:--------------------:| +| {STRING\(id), STRING\(description), STRING\(email), STRING\(name), STRING\(phone), {STRING\(city), STRING\(country), STRING\(line1), STRING\(line2), STRING\(postal_code), STRING\(state)}\(address)} | OBJECT_BUILDER | + + + + + + +### Create Invoice +Creates a new invoice. + +#### Properties + +| Name | Type | Control Type | Description | +|:--------------:|:------------:|:--------------------:|:-------------------:| +| Invoice | {STRING\(customer), STRING\(currency), STRING\(description)} | OBJECT_BUILDER | | + + +### Output + + + +Type: OBJECT + + +#### Properties + +| Type | Control Type | +|:------------:|:--------------------:| +| {STRING\(id), STRING\(customer), STRING\(currency), STRING\(description)} | OBJECT_BUILDER | + + + + + + diff --git a/server/apps/server-app/build.gradle.kts b/server/apps/server-app/build.gradle.kts index 2601d904262..eee280c758c 100644 --- a/server/apps/server-app/build.gradle.kts +++ b/server/apps/server-app/build.gradle.kts @@ -219,6 +219,7 @@ dependencies { implementation(project(":server:libs:modules:components:shopify")) implementation(project(":server:libs:modules:components:slack")) implementation(project(":server:libs:modules:components:spotify")) + implementation(project(":server:libs:modules:components:stripe")) implementation(project(":server:libs:modules:components:teamwork")) implementation(project(":server:libs:modules:components:text-helper")) implementation(project(":server:libs:modules:components:todoist")) diff --git a/server/ee/apps/worker-app/build.gradle.kts b/server/ee/apps/worker-app/build.gradle.kts index 1df9805527a..e9cebf1739b 100644 --- a/server/ee/apps/worker-app/build.gradle.kts +++ b/server/ee/apps/worker-app/build.gradle.kts @@ -140,6 +140,7 @@ dependencies { implementation(project(":server:libs:modules:components:shopify")) implementation(project(":server:libs:modules:components:slack")) implementation(project(":server:libs:modules:components:spotify")) + implementation(project(":server:libs:modules:components:stripe")) implementation(project(":server:libs:modules:components:teamwork")) implementation(project(":server:libs:modules:components:text-helper")) implementation(project(":server:libs:modules:components:todoist")) diff --git a/server/libs/modules/components/stripe/openapi.yaml b/server/libs/modules/components/stripe/openapi.yaml new file mode 100644 index 00000000000..5ceb60319d0 --- /dev/null +++ b/server/libs/modules/components/stripe/openapi.yaml @@ -0,0 +1,136 @@ +--- +openapi: "3.0.1" +info: + title: "Stripe" + description: "Stripe is a payment processing platform that allows businesses to accept online payments and manage transactions securely." + version: "v1" +servers: + - url: "https://api.stripe.com/v1" +paths: + /customers: + post: + summary: "Create Customer" + description: "Creates a new customer." + operationId: "createCustomer" + requestBody: + content: + application/x-www-form-urlencoded: + schema: + title: "Customer" + type: "object" + properties: + email: + type: "string" + description: "Customer’s email address." + maxLength: 512 + name: + type: "string" + description: "The customer's full name." + description: + type: "string" + phone: + type: "string" + address: + type: "object" + properties: + city: + type: "string" + country: + type: "string" + line1: + type: "string" + title: "Address Line 1" + line2: + type: "string" + title: "Address Line 2" + postal_code: + type: "string" + title: "Postal Code" + state: + type: "string" + description: "State, country, province, or region." + responses: + 200: + description: "Successful operation." + content: + application/json: + schema: + type: "object" + properties: + body: + type: "object" + properties: + id: + type: "string" + description: + type: "string" + email: + type: "string" + name: + type: "string" + phone: + type: "string" + address: + type: "object" + properties: + city: + type: "string" + country: + type: "string" + line1: + type: "string" + line2: + type: "string" + postal_code: + type: "string" + state: + type: "string" + /invoices: + post: + summary: "Create Invoice" + description: "Creates a new invoice." + operationId: "createInvoice" + requestBody: + content: + application/x-www-form-urlencoded: + schema: + title: "Invoice" + type: "object" + required: + - "customer_id" + - "currency" + properties: + customer: + type: "string" + title: "Customer" + description: "Customer who will be billed." + currency: + type: "string" + description: "Currency used for invoice." + description: + type: "string" + description: "Description for the invoice." + responses: + 200: + description: "Successful operation." + content: + application/json: + schema: + type: "object" + properties: + body: + type: "object" + properties: + id: + type: "string" + customer: + type: "string" + currency: + type: "string" + description: + type: "string" +components: + securitySchemes: + bearerAuth: + scheme: "bearer" + type: "http" diff --git a/server/libs/modules/components/stripe/src/main/java/com/bytechef/component/stripe/AbstractStripeComponentHandler.java b/server/libs/modules/components/stripe/src/main/java/com/bytechef/component/stripe/AbstractStripeComponentHandler.java new file mode 100644 index 00000000000..455e6db24c8 --- /dev/null +++ b/server/libs/modules/components/stripe/src/main/java/com/bytechef/component/stripe/AbstractStripeComponentHandler.java @@ -0,0 +1,47 @@ +/* + * Copyright 2023-present ByteChef Inc. + * + * 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.stripe; + +import static com.bytechef.component.definition.ComponentDsl.component; + +import com.bytechef.component.OpenApiComponentHandler; +import com.bytechef.component.definition.ComponentDefinition; +import com.bytechef.component.stripe.action.StripeCreateCustomerAction; +import com.bytechef.component.stripe.action.StripeCreateInvoiceAction; +import com.bytechef.component.stripe.connection.StripeConnection; + +/** + * Provides the base implementation for the REST based component. + * + * @generated + */ +public abstract class AbstractStripeComponentHandler implements OpenApiComponentHandler { + private final ComponentDefinition componentDefinition = modifyComponent( + component("stripe") + .title("Stripe") + .description( + "Stripe is a payment processing platform that allows businesses to accept online payments and manage transactions securely.")) + .actions(modifyActions(StripeCreateCustomerAction.ACTION_DEFINITION, + StripeCreateInvoiceAction.ACTION_DEFINITION)) + .connection(modifyConnection(StripeConnection.CONNECTION_DEFINITION)) + .triggers(getTriggers()); + + @Override + public ComponentDefinition getDefinition() { + return componentDefinition; + } +} diff --git a/server/libs/modules/components/stripe/src/main/java/com/bytechef/component/stripe/StripeComponentHandler.java b/server/libs/modules/components/stripe/src/main/java/com/bytechef/component/stripe/StripeComponentHandler.java new file mode 100644 index 00000000000..da56047481a --- /dev/null +++ b/server/libs/modules/components/stripe/src/main/java/com/bytechef/component/stripe/StripeComponentHandler.java @@ -0,0 +1,105 @@ +/* + * Copyright 2023-present ByteChef Inc. + * + * 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.stripe; + +import static com.bytechef.component.definition.Authorization.AUTHORIZATION; +import static com.bytechef.component.definition.Authorization.TOKEN; +import static com.bytechef.component.definition.ComponentDsl.authorization; +import static com.bytechef.component.definition.ComponentDsl.string; + +import com.bytechef.component.OpenApiComponentHandler; +import com.bytechef.component.definition.ActionDefinition; +import com.bytechef.component.definition.Authorization.ApplyResponse; +import com.bytechef.component.definition.Authorization.AuthorizationType; +import com.bytechef.component.definition.ComponentCategory; +import com.bytechef.component.definition.ComponentDsl.ModifiableComponentDefinition; +import com.bytechef.component.definition.ComponentDsl.ModifiableConnectionDefinition; +import com.bytechef.component.definition.ComponentDsl.ModifiableObjectProperty; +import com.bytechef.component.definition.ComponentDsl.ModifiableProperty; +import com.bytechef.component.definition.ComponentDsl.ModifiableStringProperty; +import com.bytechef.component.definition.ComponentDsl.ModifiableTriggerDefinition; +import com.bytechef.component.definition.OptionsDataSource.ActionOptionsFunction; +import com.bytechef.component.definition.Property.ValueProperty; +import com.bytechef.component.stripe.trigger.StripeNewCustomerTrigger; +import com.bytechef.component.stripe.trigger.StripeNewInvoiceTrigger; +import com.bytechef.component.stripe.util.StripeUtils; +import com.bytechef.definition.BaseProperty; +import com.google.auto.service.AutoService; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +/** + * @author Monika Kušter + */ +@AutoService(OpenApiComponentHandler.class) +public class StripeComponentHandler extends AbstractStripeComponentHandler { + + @Override + public List getTriggers() { + return List.of(StripeNewCustomerTrigger.TRIGGER_DEFINITION, StripeNewInvoiceTrigger.TRIGGER_DEFINITION); + } + + @Override + public ModifiableComponentDefinition modifyComponent(ModifiableComponentDefinition modifiableComponentDefinition) { + return modifiableComponentDefinition + .customAction(true) + .icon("path:assets/stripe.svg") + .categories(ComponentCategory.PAYMENT_PROCESSING); + } + + @Override + public ModifiableConnectionDefinition modifyConnection( + ModifiableConnectionDefinition modifiableConnectionDefinition) { + + return modifiableConnectionDefinition + .baseUri((connectionParameters, context) -> "https://api.stripe.com/v1") + .authorizations( + authorization(AuthorizationType.BEARER_TOKEN) + .title("Bearer Token") + .properties( + string(TOKEN) + .label("Token") + .required(true)) + .apply((connectionParameters, context) -> ApplyResponse + .ofHeaders( + Map.of( + "Content-Type", List.of("application/x-www-form-urlencoded"), + AUTHORIZATION, List.of("Bearer " + connectionParameters.getRequiredString(TOKEN)))))); + } + + @Override + public ModifiableProperty modifyProperty( + ActionDefinition actionDefinition, ModifiableProperty modifiableProperty) { + + if (Objects.equals(modifiableProperty.getName(), "__item")) { + Optional>> propertiesOptional = + ((ModifiableObjectProperty) modifiableProperty).getProperties(); + + for (BaseProperty baseProperty : propertiesOptional.get()) { + if (Objects.equals(baseProperty.getName(), "customer")) { + ((ModifiableStringProperty) baseProperty) + .options((ActionOptionsFunction) StripeUtils::getCustomerOptions); + } + } + } + + return modifiableProperty; + } + +} diff --git a/server/libs/modules/components/stripe/src/main/java/com/bytechef/component/stripe/action/StripeCreateCustomerAction.java b/server/libs/modules/components/stripe/src/main/java/com/bytechef/component/stripe/action/StripeCreateCustomerAction.java new file mode 100644 index 00000000000..3b6e2a41310 --- /dev/null +++ b/server/libs/modules/components/stripe/src/main/java/com/bytechef/component/stripe/action/StripeCreateCustomerAction.java @@ -0,0 +1,92 @@ +/* + * Copyright 2023-present ByteChef Inc. + * + * 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.stripe.action; + +import static com.bytechef.component.OpenApiComponentHandler.PropertyType; +import static com.bytechef.component.definition.ComponentDsl.action; +import static com.bytechef.component.definition.ComponentDsl.object; +import static com.bytechef.component.definition.ComponentDsl.outputSchema; +import static com.bytechef.component.definition.ComponentDsl.string; +import static com.bytechef.component.definition.Context.Http.BodyContentType; +import static com.bytechef.component.definition.Context.Http.ResponseType; + +import com.bytechef.component.definition.ComponentDsl; +import java.util.Map; + +/** + * Provides a list of the component actions. + * + * @generated + */ +public class StripeCreateCustomerAction { + public static final ComponentDsl.ModifiableActionDefinition ACTION_DEFINITION = action("createCustomer") + .title("Create Customer") + .description("Creates a new customer.") + .metadata( + Map.of( + "method", "POST", + "path", "/customers", "bodyContentType", BodyContentType.FORM_URL_ENCODED, "mimeType", + "application/x-www-form-urlencoded" + + )) + .properties(object("__item").properties(string("email").maxLength(512) + .label("Email") + .description("Customer’s email address.") + .required(false), + string("name").label("Name") + .description("The customer's full name.") + .required(false), + string("description").label("Description") + .required(false), + string("phone").label("Phone") + .required(false), + object("address").properties(string("city").label("City") + .required(false), + string("country").label("Country") + .required(false), + string("line1").label("Address Line 1") + .required(false), + string("line2").label("Address Line 2") + .required(false), + string("postal_code").label("Postal Code") + .required(false), + string("state").label("State") + .description("State, country, province, or region.") + .required(false)) + .label("Address") + .required(false)) + .label("Customer") + .metadata( + Map.of( + "type", PropertyType.BODY))) + .output(outputSchema(object() + .properties(object("body") + .properties(string("id").required(false), string("description").required(false), + string("email").required(false), string("name").required(false), string("phone").required(false), + object("address") + .properties(string("city").required(false), string("country").required(false), + string("line1").required(false), string("line2").required(false), + string("postal_code").required(false), string("state").required(false)) + .required(false)) + .required(false)) + .metadata( + Map.of( + "responseType", ResponseType.JSON)))); + + private StripeCreateCustomerAction() { + } +} diff --git a/server/libs/modules/components/stripe/src/main/java/com/bytechef/component/stripe/action/StripeCreateInvoiceAction.java b/server/libs/modules/components/stripe/src/main/java/com/bytechef/component/stripe/action/StripeCreateInvoiceAction.java new file mode 100644 index 00000000000..8201cbd29af --- /dev/null +++ b/server/libs/modules/components/stripe/src/main/java/com/bytechef/component/stripe/action/StripeCreateInvoiceAction.java @@ -0,0 +1,70 @@ +/* + * Copyright 2023-present ByteChef Inc. + * + * 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.stripe.action; + +import static com.bytechef.component.OpenApiComponentHandler.PropertyType; +import static com.bytechef.component.definition.ComponentDsl.action; +import static com.bytechef.component.definition.ComponentDsl.object; +import static com.bytechef.component.definition.ComponentDsl.outputSchema; +import static com.bytechef.component.definition.ComponentDsl.string; +import static com.bytechef.component.definition.Context.Http.BodyContentType; +import static com.bytechef.component.definition.Context.Http.ResponseType; + +import com.bytechef.component.definition.ComponentDsl; +import java.util.Map; + +/** + * Provides a list of the component actions. + * + * @generated + */ +public class StripeCreateInvoiceAction { + public static final ComponentDsl.ModifiableActionDefinition ACTION_DEFINITION = action("createInvoice") + .title("Create Invoice") + .description("Creates a new invoice.") + .metadata( + Map.of( + "method", "POST", + "path", "/invoices", "bodyContentType", BodyContentType.FORM_URL_ENCODED, "mimeType", + "application/x-www-form-urlencoded" + + )) + .properties(object("__item").properties(string("customer").label("Customer") + .description("Customer who will be billed.") + .required(false), + string("currency").label("Currency") + .description("Currency used for invoice.") + .required(true), + string("description").label("Description") + .description("Description for the invoice.") + .required(false)) + .label("Invoice") + .metadata( + Map.of( + "type", PropertyType.BODY))) + .output(outputSchema(object() + .properties(object("body") + .properties(string("id").required(false), string("customer").required(false), + string("currency").required(false), string("description").required(false)) + .required(false)) + .metadata( + Map.of( + "responseType", ResponseType.JSON)))); + + private StripeCreateInvoiceAction() { + } +} diff --git a/server/libs/modules/components/stripe/src/main/java/com/bytechef/component/stripe/connection/StripeConnection.java b/server/libs/modules/components/stripe/src/main/java/com/bytechef/component/stripe/connection/StripeConnection.java new file mode 100644 index 00000000000..f205446a312 --- /dev/null +++ b/server/libs/modules/components/stripe/src/main/java/com/bytechef/component/stripe/connection/StripeConnection.java @@ -0,0 +1,44 @@ +/* + * Copyright 2023-present ByteChef Inc. + * + * 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.stripe.connection; + +import static com.bytechef.component.definition.Authorization.AuthorizationType; +import static com.bytechef.component.definition.Authorization.TOKEN; +import static com.bytechef.component.definition.ComponentDsl.authorization; +import static com.bytechef.component.definition.ComponentDsl.connection; +import static com.bytechef.component.definition.ComponentDsl.string; + +import com.bytechef.component.definition.ComponentDsl; + +/** + * Provides the component connection definition. + * + * @generated + */ +public class StripeConnection { + public static final ComponentDsl.ModifiableConnectionDefinition CONNECTION_DEFINITION = connection() + .baseUri((connectionParameters, context) -> "https://api.stripe.com/v1") + .authorizations(authorization(AuthorizationType.BEARER_TOKEN) + .title("Bearer Token") + .properties( + string(TOKEN) + .label("Token") + .required(true))); + + private StripeConnection() { + } +} diff --git a/server/libs/modules/components/stripe/src/main/java/com/bytechef/component/stripe/constant/StripeConstants.java b/server/libs/modules/components/stripe/src/main/java/com/bytechef/component/stripe/constant/StripeConstants.java new file mode 100644 index 00000000000..07f1801163b --- /dev/null +++ b/server/libs/modules/components/stripe/src/main/java/com/bytechef/component/stripe/constant/StripeConstants.java @@ -0,0 +1,28 @@ +/* + * Copyright 2023-present ByteChef Inc. + * + * 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.stripe.constant; + +/** + * @author Monika Kušter + */ +public class StripeConstants { + + public static final String ID = "id"; + + private StripeConstants() { + } +} diff --git a/server/libs/modules/components/stripe/src/main/java/com/bytechef/component/stripe/trigger/StripeNewCustomerTrigger.java b/server/libs/modules/components/stripe/src/main/java/com/bytechef/component/stripe/trigger/StripeNewCustomerTrigger.java new file mode 100644 index 00000000000..4a0b0d13e36 --- /dev/null +++ b/server/libs/modules/components/stripe/src/main/java/com/bytechef/component/stripe/trigger/StripeNewCustomerTrigger.java @@ -0,0 +1,92 @@ +/* + * Copyright 2023-present ByteChef Inc. + * + * 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.stripe.trigger; + +import static com.bytechef.component.definition.ComponentDsl.object; +import static com.bytechef.component.definition.ComponentDsl.outputSchema; +import static com.bytechef.component.definition.ComponentDsl.string; +import static com.bytechef.component.definition.ComponentDsl.trigger; +import static com.bytechef.component.stripe.constant.StripeConstants.ID; + +import com.bytechef.component.definition.ComponentDsl.ModifiableTriggerDefinition; +import com.bytechef.component.definition.Parameters; +import com.bytechef.component.definition.TriggerContext; +import com.bytechef.component.definition.TriggerDefinition.HttpHeaders; +import com.bytechef.component.definition.TriggerDefinition.HttpParameters; +import com.bytechef.component.definition.TriggerDefinition.TriggerType; +import com.bytechef.component.definition.TriggerDefinition.WebhookBody; +import com.bytechef.component.definition.TriggerDefinition.WebhookEnableOutput; +import com.bytechef.component.definition.TriggerDefinition.WebhookMethod; +import com.bytechef.component.stripe.util.StripeUtils; +import java.util.Map; + +/** + * @author Monika Kušter + */ +public class StripeNewCustomerTrigger { + + public static final ModifiableTriggerDefinition TRIGGER_DEFINITION = trigger("newCustomer") + .title("New Customer") + .description("Triggers when a new customer is created.") + .type(TriggerType.DYNAMIC_WEBHOOK) + .output( + outputSchema( + object() + .properties( + string(ID), + string("object"), + string("description"), + string("email"), + string("name"), + string("phone"), + object("address") + .properties( + string("city"), + string("country"), + string("line1"), + string("line2"), + string("postal_code"), + string("state"))))) + .webhookEnable(StripeNewCustomerTrigger::webhookEnable) + .webhookDisable(StripeNewCustomerTrigger::webhookDisable) + .webhookRequest(StripeNewCustomerTrigger::webhookRequest); + + private StripeNewCustomerTrigger() { + } + + protected static WebhookEnableOutput webhookEnable( + Parameters inputParameters, Parameters connectionParameters, String webhookUrl, String workflowExecutionId, + TriggerContext context) { + + return new WebhookEnableOutput( + Map.of(ID, StripeUtils.subscribeWebhook(webhookUrl, context, "customer.created")), null); + } + + protected static void webhookDisable( + Parameters inputParameters, Parameters connectionParameters, Parameters outputParameters, + String workflowExecutionId, TriggerContext context) { + + StripeUtils.unsubscribeWebhook(outputParameters.getString(ID), context); + } + + protected static Object webhookRequest( + Parameters inputParameters, Parameters connectionParameters, HttpHeaders headers, HttpParameters parameters, + WebhookBody body, WebhookMethod method, WebhookEnableOutput output, TriggerContext context) { + + return StripeUtils.getNewObject(body); + } +} diff --git a/server/libs/modules/components/stripe/src/main/java/com/bytechef/component/stripe/trigger/StripeNewInvoiceTrigger.java b/server/libs/modules/components/stripe/src/main/java/com/bytechef/component/stripe/trigger/StripeNewInvoiceTrigger.java new file mode 100644 index 00000000000..f9188e0805e --- /dev/null +++ b/server/libs/modules/components/stripe/src/main/java/com/bytechef/component/stripe/trigger/StripeNewInvoiceTrigger.java @@ -0,0 +1,84 @@ +/* + * Copyright 2023-present ByteChef Inc. + * + * 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.stripe.trigger; + +import static com.bytechef.component.definition.ComponentDsl.object; +import static com.bytechef.component.definition.ComponentDsl.outputSchema; +import static com.bytechef.component.definition.ComponentDsl.string; +import static com.bytechef.component.definition.ComponentDsl.trigger; +import static com.bytechef.component.stripe.constant.StripeConstants.ID; + +import com.bytechef.component.definition.ComponentDsl.ModifiableTriggerDefinition; +import com.bytechef.component.definition.Parameters; +import com.bytechef.component.definition.TriggerContext; +import com.bytechef.component.definition.TriggerDefinition.HttpHeaders; +import com.bytechef.component.definition.TriggerDefinition.HttpParameters; +import com.bytechef.component.definition.TriggerDefinition.TriggerType; +import com.bytechef.component.definition.TriggerDefinition.WebhookBody; +import com.bytechef.component.definition.TriggerDefinition.WebhookEnableOutput; +import com.bytechef.component.definition.TriggerDefinition.WebhookMethod; +import com.bytechef.component.stripe.util.StripeUtils; +import java.util.Map; + +/** + * @author Monika Kušter + */ +public class StripeNewInvoiceTrigger { + + public static final ModifiableTriggerDefinition TRIGGER_DEFINITION = trigger("newInvoice") + .title("New Invoice") + .description("Triggers on a new invoice.") + .type(TriggerType.DYNAMIC_WEBHOOK) + .output( + outputSchema( + object() + .properties( + string(ID), + string("object"), + string("currency"), + string("customer"), + string("customer_name"), + string("description")))) + .webhookEnable(StripeNewInvoiceTrigger::webhookEnable) + .webhookDisable(StripeNewInvoiceTrigger::webhookDisable) + .webhookRequest(StripeNewInvoiceTrigger::webhookRequest); + + private StripeNewInvoiceTrigger() { + } + + protected static WebhookEnableOutput webhookEnable( + Parameters inputParameters, Parameters connectionParameters, String webhookUrl, String workflowExecutionId, + TriggerContext context) { + + return new WebhookEnableOutput( + Map.of(ID, StripeUtils.subscribeWebhook(webhookUrl, context, "invoice.created")), null); + } + + protected static void webhookDisable( + Parameters inputParameters, Parameters connectionParameters, Parameters outputParameters, + String workflowExecutionId, TriggerContext context) { + + StripeUtils.unsubscribeWebhook(outputParameters.getString(ID), context); + } + + protected static Object webhookRequest( + Parameters inputParameters, Parameters connectionParameters, HttpHeaders headers, HttpParameters parameters, + WebhookBody body, WebhookMethod method, WebhookEnableOutput output, TriggerContext context) { + + return StripeUtils.getNewObject(body); + } +} diff --git a/server/libs/modules/components/stripe/src/main/java/com/bytechef/component/stripe/util/StripeUtils.java b/server/libs/modules/components/stripe/src/main/java/com/bytechef/component/stripe/util/StripeUtils.java new file mode 100644 index 00000000000..dce3e8f3166 --- /dev/null +++ b/server/libs/modules/components/stripe/src/main/java/com/bytechef/component/stripe/util/StripeUtils.java @@ -0,0 +1,94 @@ +/* + * Copyright 2023-present ByteChef Inc. + * + * 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.stripe.util; + +import static com.bytechef.component.definition.ComponentDsl.option; +import static com.bytechef.component.definition.Context.Http.responseType; +import static com.bytechef.component.stripe.constant.StripeConstants.ID; + +import com.bytechef.component.definition.ActionContext; +import com.bytechef.component.definition.Context.Http; +import com.bytechef.component.definition.Context.Http.ResponseType; +import com.bytechef.component.definition.Option; +import com.bytechef.component.definition.Parameters; +import com.bytechef.component.definition.TriggerContext; +import com.bytechef.component.definition.TriggerDefinition.WebhookBody; +import com.bytechef.component.definition.TypeReference; +import com.bytechef.component.exception.ProviderException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * @author Monika Kušter + */ +public class StripeUtils { + + public static List> getCustomerOptions( + Parameters inputParameters, Parameters connectionParameters, Map dependencyPaths, + String searchText, ActionContext actionContext) { + + Map body = actionContext.http( + http -> http.get("/customers")) + .configuration(responseType(ResponseType.JSON)) + .execute() + .getBody(new TypeReference<>() {}); + + List> options = new ArrayList<>(); + + if (body.get("data") instanceof List list) { + for (Object object : list) { + if (object instanceof Map map) { + options.add(option((String) map.get("name"), (String) map.get(ID))); + } + } + } + return options; + + } + + public static Object getNewObject(WebhookBody webhookBody) { + Map content = webhookBody.getContent(new TypeReference<>() {}); + Object data = content.get("data"); + + if (data instanceof Map map) { + return map.get("object"); + } + + throw new ProviderException("Stripe webhook request is not valid."); + } + + public static String subscribeWebhook(String webhookUrl, TriggerContext context, String event) { + Map body = context + .http(http -> http.post("/webhook_endpoints")) + .body( + Http.Body.of( + Map.of("enabled_events", List.of(event), "url", webhookUrl), + Http.BodyContentType.FORM_URL_ENCODED)) + .configuration(responseType(ResponseType.JSON)) + .execute() + .getBody(new TypeReference<>() {}); + + return (String) body.get(ID); + } + + public static void unsubscribeWebhook(String webhookId, TriggerContext context) { + context.http(http -> http.delete("/webhook_endpoints/" + webhookId)) + .configuration(responseType(ResponseType.JSON)) + .execute(); + } +} diff --git a/server/libs/modules/components/stripe/src/main/resources/assets/stripe.svg b/server/libs/modules/components/stripe/src/main/resources/assets/stripe.svg new file mode 100644 index 00000000000..37b894f9e83 --- /dev/null +++ b/server/libs/modules/components/stripe/src/main/resources/assets/stripe.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + diff --git a/server/libs/modules/components/stripe/src/test/java/com/bytechef/component/stripe/AbstractStripeComponentHandlerTest.java b/server/libs/modules/components/stripe/src/test/java/com/bytechef/component/stripe/AbstractStripeComponentHandlerTest.java new file mode 100644 index 00000000000..1038f2129d3 --- /dev/null +++ b/server/libs/modules/components/stripe/src/test/java/com/bytechef/component/stripe/AbstractStripeComponentHandlerTest.java @@ -0,0 +1,32 @@ +/* + * Copyright 2023-present ByteChef Inc. + * + * 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.stripe; + +import com.bytechef.test.jsonasssert.JsonFileAssert; +import org.junit.jupiter.api.Test; + +/** + * Provides the base test implementation for the REST based component. + * + * @generated + */ +public abstract class AbstractStripeComponentHandlerTest { + @Test + public void testGetDefinition() { + JsonFileAssert.assertEquals("definition/stripe_v1.json", new StripeComponentHandler().getDefinition()); + } +} diff --git a/server/libs/modules/components/stripe/src/test/java/com/bytechef/component/stripe/StripeComponentHandlerTest.java b/server/libs/modules/components/stripe/src/test/java/com/bytechef/component/stripe/StripeComponentHandlerTest.java new file mode 100644 index 00000000000..7e4bcd486f5 --- /dev/null +++ b/server/libs/modules/components/stripe/src/test/java/com/bytechef/component/stripe/StripeComponentHandlerTest.java @@ -0,0 +1,23 @@ +/* + * Copyright 2023-present ByteChef Inc. + * + * 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.stripe; + +/** + * @generated + */ +public class StripeComponentHandlerTest extends AbstractStripeComponentHandlerTest { +} diff --git a/server/libs/modules/components/stripe/src/test/java/com/bytechef/component/stripe/trigger/AbstractStripeTriggerTest.java b/server/libs/modules/components/stripe/src/test/java/com/bytechef/component/stripe/trigger/AbstractStripeTriggerTest.java new file mode 100644 index 00000000000..1bef1b4481b --- /dev/null +++ b/server/libs/modules/components/stripe/src/test/java/com/bytechef/component/stripe/trigger/AbstractStripeTriggerTest.java @@ -0,0 +1,62 @@ +/* + * Copyright 2023-present ByteChef Inc. + * + * 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.stripe.trigger; + +import static com.bytechef.component.stripe.constant.StripeConstants.ID; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; + +import com.bytechef.component.definition.Parameters; +import com.bytechef.component.definition.TriggerContext; +import com.bytechef.component.definition.TriggerDefinition.HttpHeaders; +import com.bytechef.component.definition.TriggerDefinition.HttpParameters; +import com.bytechef.component.definition.TriggerDefinition.WebhookBody; +import com.bytechef.component.definition.TriggerDefinition.WebhookEnableOutput; +import com.bytechef.component.definition.TriggerDefinition.WebhookMethod; +import com.bytechef.component.stripe.util.StripeUtils; +import com.bytechef.component.test.definition.MockParametersFactory; +import java.util.Map; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.mockito.MockedStatic; + +/** + * @author Monika Kušter + */ +abstract class AbstractStripeTriggerTest { + + protected WebhookEnableOutput mockedWebhookEnableOutput = mock(WebhookEnableOutput.class); + protected WebhookBody mockedWebhookBody = mock(WebhookBody.class); + protected HttpHeaders mockedHttpHeaders = mock(HttpHeaders.class); + protected HttpParameters mockedHttpParameters = mock(HttpParameters.class); + protected WebhookMethod mockedWebhookMethod = mock(WebhookMethod.class); + protected Object mockedObject = mock(Object.class); + protected Parameters mockedParameters = MockParametersFactory.create(Map.of(ID, "abc")); + protected TriggerContext mockedTriggerContext = mock(TriggerContext.class); + protected MockedStatic stripeUtilsMockedStatic; + protected String workflowExecutionId = "testWorkflowExecutionId"; + + @BeforeEach + public void beforeEach() { + stripeUtilsMockedStatic = mockStatic(StripeUtils.class); + } + + @AfterEach + public void afterEach() { + stripeUtilsMockedStatic.close(); + } +} diff --git a/server/libs/modules/components/stripe/src/test/java/com/bytechef/component/stripe/trigger/StripeNewCustomerTriggerTest.java b/server/libs/modules/components/stripe/src/test/java/com/bytechef/component/stripe/trigger/StripeNewCustomerTriggerTest.java new file mode 100644 index 00000000000..f4583e1cb1d --- /dev/null +++ b/server/libs/modules/components/stripe/src/test/java/com/bytechef/component/stripe/trigger/StripeNewCustomerTriggerTest.java @@ -0,0 +1,75 @@ +/* + * Copyright 2023-present ByteChef Inc. + * + * 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.stripe.trigger; + +import static com.bytechef.component.stripe.constant.StripeConstants.ID; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import com.bytechef.component.definition.TriggerDefinition.WebhookEnableOutput; +import com.bytechef.component.stripe.util.StripeUtils; +import java.time.LocalDateTime; +import java.util.Map; +import org.junit.jupiter.api.Test; + +/** + * @author Monika Kušter + */ +class StripeNewCustomerTriggerTest extends AbstractStripeTriggerTest { + + @Test + void testWebhookEnable() { + String webhookUrl = "testWebhookUrl"; + + stripeUtilsMockedStatic.when( + () -> StripeUtils.subscribeWebhook(webhookUrl, mockedTriggerContext, "customer.created")) + .thenReturn("123"); + + WebhookEnableOutput webhookEnableOutput = StripeNewCustomerTrigger.webhookEnable( + mockedParameters, mockedParameters, webhookUrl, workflowExecutionId, mockedTriggerContext); + + Map parameters = webhookEnableOutput.parameters(); + LocalDateTime webhookExpirationDate = webhookEnableOutput.webhookExpirationDate(); + + Map expectedParameters = Map.of(ID, "123"); + + assertEquals(expectedParameters, parameters); + assertNull(webhookExpirationDate); + } + + @Test + void testWebhookDisable() { + StripeNewCustomerTrigger.webhookDisable( + mockedParameters, mockedParameters, mockedParameters, workflowExecutionId, mockedTriggerContext); + + stripeUtilsMockedStatic + .verify(() -> StripeUtils.unsubscribeWebhook("abc", mockedTriggerContext)); + } + + @Test + void testWebhookRequest() { + stripeUtilsMockedStatic.when( + () -> StripeUtils.getNewObject(mockedWebhookBody)) + .thenReturn(mockedObject); + + Object result = StripeNewCustomerTrigger.webhookRequest( + mockedParameters, mockedParameters, mockedHttpHeaders, mockedHttpParameters, mockedWebhookBody, + mockedWebhookMethod, mockedWebhookEnableOutput, mockedTriggerContext); + + assertEquals(mockedObject, result); + } +} diff --git a/server/libs/modules/components/stripe/src/test/java/com/bytechef/component/stripe/trigger/StripeNewInvoiceTriggerTest.java b/server/libs/modules/components/stripe/src/test/java/com/bytechef/component/stripe/trigger/StripeNewInvoiceTriggerTest.java new file mode 100644 index 00000000000..2792a0d4adf --- /dev/null +++ b/server/libs/modules/components/stripe/src/test/java/com/bytechef/component/stripe/trigger/StripeNewInvoiceTriggerTest.java @@ -0,0 +1,75 @@ +/* + * Copyright 2023-present ByteChef Inc. + * + * 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.stripe.trigger; + +import static com.bytechef.component.stripe.constant.StripeConstants.ID; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import com.bytechef.component.definition.TriggerDefinition.WebhookEnableOutput; +import com.bytechef.component.stripe.util.StripeUtils; +import java.time.LocalDateTime; +import java.util.Map; +import org.junit.jupiter.api.Test; + +/** + * @author Monika Kušter + */ +class StripeNewInvoiceTriggerTest extends AbstractStripeTriggerTest { + + @Test + void testWebhookEnable() { + String webhookUrl = "testWebhookUrl"; + + stripeUtilsMockedStatic.when( + () -> StripeUtils.subscribeWebhook(webhookUrl, mockedTriggerContext, "invoice.created")) + .thenReturn("123"); + + WebhookEnableOutput webhookEnableOutput = StripeNewInvoiceTrigger.webhookEnable( + mockedParameters, mockedParameters, webhookUrl, workflowExecutionId, mockedTriggerContext); + + Map parameters = webhookEnableOutput.parameters(); + LocalDateTime webhookExpirationDate = webhookEnableOutput.webhookExpirationDate(); + + Map expectedParameters = Map.of(ID, "123"); + + assertEquals(expectedParameters, parameters); + assertNull(webhookExpirationDate); + } + + @Test + void testWebhookDisable() { + StripeNewInvoiceTrigger.webhookDisable( + mockedParameters, mockedParameters, mockedParameters, workflowExecutionId, mockedTriggerContext); + + stripeUtilsMockedStatic + .verify(() -> StripeUtils.unsubscribeWebhook("abc", mockedTriggerContext)); + } + + @Test + void testWebhookRequest() { + stripeUtilsMockedStatic.when( + () -> StripeUtils.getNewObject(mockedWebhookBody)) + .thenReturn(mockedObject); + + Object result = StripeNewInvoiceTrigger.webhookRequest( + mockedParameters, mockedParameters, mockedHttpHeaders, mockedHttpParameters, mockedWebhookBody, + mockedWebhookMethod, mockedWebhookEnableOutput, mockedTriggerContext); + + assertEquals(mockedObject, result); + } +} diff --git a/server/libs/modules/components/stripe/src/test/java/com/bytechef/component/stripe/util/StripeUtilsTest.java b/server/libs/modules/components/stripe/src/test/java/com/bytechef/component/stripe/util/StripeUtilsTest.java new file mode 100644 index 00000000000..292769d6691 --- /dev/null +++ b/server/libs/modules/components/stripe/src/test/java/com/bytechef/component/stripe/util/StripeUtilsTest.java @@ -0,0 +1,108 @@ +/* + * Copyright 2023-present ByteChef Inc. + * + * 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.stripe.util; + +import static com.bytechef.component.definition.ComponentDsl.option; +import static com.bytechef.component.stripe.constant.StripeConstants.ID; +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.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.bytechef.component.definition.ActionContext; +import com.bytechef.component.definition.Context.Http; +import com.bytechef.component.definition.Parameters; +import com.bytechef.component.definition.TriggerContext; +import com.bytechef.component.definition.TriggerDefinition.WebhookBody; +import com.bytechef.component.definition.TypeReference; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; + +/** + * * @author Monika Kušter + */ +class StripeUtilsTest { + + private final ArgumentCaptor bodyArgumentCaptor = ArgumentCaptor.forClass(Http.Body.class); + private final ActionContext mockedActionContext = mock(ActionContext.class); + private final Http.Executor mockedExecutor = mock(Http.Executor.class); + private final Object mockedObject = mock(Object.class); + private final Parameters mockedParameters = mock(Parameters.class); + private final Http.Response mockedResponse = mock(Http.Response.class); + private final TriggerContext mockedTriggerContext = mock(TriggerContext.class); + protected WebhookBody mockedWebhookBody = mock(WebhookBody.class); + + @BeforeEach() + void beforeEach() { + when(mockedActionContext.http(any())) + .thenReturn(mockedExecutor); + when(mockedTriggerContext.http(any())) + .thenReturn(mockedExecutor); + when(mockedExecutor.body(bodyArgumentCaptor.capture())) + .thenReturn(mockedExecutor); + when(mockedExecutor.configuration(any())) + .thenReturn(mockedExecutor); + when(mockedExecutor.execute()) + .thenReturn(mockedResponse); + } + + @Test + void testGetCustomerOptions() { + when(mockedResponse.getBody(any(TypeReference.class))) + .thenReturn(Map.of("data", List.of(Map.of("name", "some name", ID, "abc")))); + + assertEquals(List.of(option("some name", "abc")), + StripeUtils.getCustomerOptions(mockedParameters, mockedParameters, Map.of(), "", mockedActionContext)); + } + + @Test + void testGetNewObject() { + when(mockedWebhookBody.getContent(any(TypeReference.class))) + .thenReturn(Map.of("data", Map.of("object", mockedObject))); + + assertEquals(mockedObject, StripeUtils.getNewObject(mockedWebhookBody)); + } + + @Test + void testSubscribeWebhook() { + when(mockedResponse.getBody(any(TypeReference.class))) + .thenReturn(Map.of(ID, "123")); + + String id = StripeUtils.subscribeWebhook("webhookUrl", mockedTriggerContext, "event"); + + assertEquals("123", id); + + Http.Body body = bodyArgumentCaptor.getValue(); + + assertEquals(Map.of("enabled_events", List.of("event"), "url", "webhookUrl"), body.getContent()); + assertEquals(Http.BodyContentType.FORM_URL_ENCODED, body.getContentType()); + } + + @Test + void testUnsubscribeWebhook() { + StripeUtils.unsubscribeWebhook("", mockedTriggerContext); + + verify(mockedTriggerContext, times(1)).http(any()); + verify(mockedExecutor, times(1)).configuration(any()); + verify(mockedExecutor, times(1)).execute(); + } +} diff --git a/server/libs/modules/components/stripe/src/test/resources/definition/stripe_v1.json b/server/libs/modules/components/stripe/src/test/resources/definition/stripe_v1.json new file mode 100644 index 00000000000..e36294de09c --- /dev/null +++ b/server/libs/modules/components/stripe/src/test/resources/definition/stripe_v1.json @@ -0,0 +1,2160 @@ +{ + "categories" : [ { + "key" : "payment-processing", + "label" : "payment-processing" + } ], + "customAction" : true, + "customActionHelp" : null, + "description" : "Stripe is a payment processing platform that allows businesses to accept online payments and manage transactions securely.", + "icon" : "path:assets/stripe.svg", + "tags" : null, + "metadata" : null, + "name" : "stripe", + "resources" : null, + "version" : 1, + "title" : "Stripe", + "actions" : [ { + "batch" : null, + "deprecated" : null, + "description" : "Creates a new customer.", + "help" : null, + "metadata" : { + "path" : "/customers", + "mimeType" : "application/x-www-form-urlencoded", + "method" : "POST", + "bodyContentType" : "FORM_URL_ENCODED" + }, + "name" : "createCustomer", + "outputDefinition" : { + "output" : null, + "outputResponse" : { + "outputSchema" : { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { + "responseType" : "JSON" + }, + "required" : null, + "name" : null, + "type" : "OBJECT", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "additionalProperties" : null, + "multipleValues" : null, + "options" : null, + "properties" : [ { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "body", + "type" : "OBJECT", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "additionalProperties" : null, + "multipleValues" : null, + "options" : null, + "properties" : [ { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "id", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "description", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "email", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "name", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "phone", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "address", + "type" : "OBJECT", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "additionalProperties" : null, + "multipleValues" : null, + "options" : null, + "properties" : [ { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "city", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "country", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "line1", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "line2", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "postal_code", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "state", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + } ], + "controlType" : "OBJECT_BUILDER", + "optionsDataSource" : null + } ], + "controlType" : "OBJECT_BUILDER", + "optionsDataSource" : null + } ], + "controlType" : "OBJECT_BUILDER", + "optionsDataSource" : null + }, + "sampleOutput" : null + }, + "outputSchema" : { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { + "responseType" : "JSON" + }, + "required" : null, + "name" : null, + "type" : "OBJECT", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "additionalProperties" : null, + "multipleValues" : null, + "options" : null, + "properties" : [ { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "body", + "type" : "OBJECT", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "additionalProperties" : null, + "multipleValues" : null, + "options" : null, + "properties" : [ { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "id", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "description", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "email", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "name", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "phone", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "address", + "type" : "OBJECT", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "additionalProperties" : null, + "multipleValues" : null, + "options" : null, + "properties" : [ { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "city", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "country", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "line1", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "line2", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "postal_code", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "state", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + } ], + "controlType" : "OBJECT_BUILDER", + "optionsDataSource" : null + } ], + "controlType" : "OBJECT_BUILDER", + "optionsDataSource" : null + } ], + "controlType" : "OBJECT_BUILDER", + "optionsDataSource" : null + }, + "sampleOutput" : null + }, + "properties" : [ { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { + "type" : "BODY" + }, + "required" : null, + "name" : "__item", + "type" : "OBJECT", + "defaultValue" : null, + "exampleValue" : null, + "label" : "Customer", + "placeholder" : null, + "additionalProperties" : null, + "multipleValues" : null, + "options" : null, + "properties" : [ { + "advancedOption" : null, + "description" : "Customer’s email address.", + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "email", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : "Email", + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : 512, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : "The customer's full name.", + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "name", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : "Name", + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "description", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : "Description", + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "phone", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : "Phone", + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "address", + "type" : "OBJECT", + "defaultValue" : null, + "exampleValue" : null, + "label" : "Address", + "placeholder" : null, + "additionalProperties" : null, + "multipleValues" : null, + "options" : null, + "properties" : [ { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "city", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : "City", + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "country", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : "Country", + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "line1", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : "Address Line 1", + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "line2", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : "Address Line 2", + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "postal_code", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : "Postal Code", + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : "State, country, province, or region.", + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "state", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : "State", + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + } ], + "controlType" : "OBJECT_BUILDER", + "optionsDataSource" : null + } ], + "controlType" : "OBJECT_BUILDER", + "optionsDataSource" : null + } ], + "title" : "Create Customer", + "perform" : null, + "workflowNodeDescription" : null, + "processErrorResponse" : null + }, { + "batch" : null, + "deprecated" : null, + "description" : "Creates a new invoice.", + "help" : null, + "metadata" : { + "path" : "/invoices", + "mimeType" : "application/x-www-form-urlencoded", + "method" : "POST", + "bodyContentType" : "FORM_URL_ENCODED" + }, + "name" : "createInvoice", + "outputDefinition" : { + "output" : null, + "outputResponse" : { + "outputSchema" : { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { + "responseType" : "JSON" + }, + "required" : null, + "name" : null, + "type" : "OBJECT", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "additionalProperties" : null, + "multipleValues" : null, + "options" : null, + "properties" : [ { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "body", + "type" : "OBJECT", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "additionalProperties" : null, + "multipleValues" : null, + "options" : null, + "properties" : [ { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "id", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "customer", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "currency", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "description", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + } ], + "controlType" : "OBJECT_BUILDER", + "optionsDataSource" : null + } ], + "controlType" : "OBJECT_BUILDER", + "optionsDataSource" : null + }, + "sampleOutput" : null + }, + "outputSchema" : { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { + "responseType" : "JSON" + }, + "required" : null, + "name" : null, + "type" : "OBJECT", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "additionalProperties" : null, + "multipleValues" : null, + "options" : null, + "properties" : [ { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "body", + "type" : "OBJECT", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "additionalProperties" : null, + "multipleValues" : null, + "options" : null, + "properties" : [ { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "id", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "customer", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "currency", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "description", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + } ], + "controlType" : "OBJECT_BUILDER", + "optionsDataSource" : null + } ], + "controlType" : "OBJECT_BUILDER", + "optionsDataSource" : null + }, + "sampleOutput" : null + }, + "properties" : [ { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { + "type" : "BODY" + }, + "required" : null, + "name" : "__item", + "type" : "OBJECT", + "defaultValue" : null, + "exampleValue" : null, + "label" : "Invoice", + "placeholder" : null, + "additionalProperties" : null, + "multipleValues" : null, + "options" : null, + "properties" : [ { + "advancedOption" : null, + "description" : "Customer who will be billed.", + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "customer", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : "Customer", + "placeholder" : null, + "controlType" : "SELECT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : { + "optionsLookupDependsOn" : null, + "options" : { } + } + }, { + "advancedOption" : null, + "description" : "Currency used for invoice.", + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : true, + "name" : "currency", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : "Currency", + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : "Description for the invoice.", + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : false, + "name" : "description", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : "Description", + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + } ], + "controlType" : "OBJECT_BUILDER", + "optionsDataSource" : null + } ], + "title" : "Create Invoice", + "perform" : null, + "workflowNodeDescription" : null, + "processErrorResponse" : null + } ], + "dataStream" : null, + "unifiedApi" : null, + "triggers" : [ { + "batch" : null, + "deprecated" : null, + "description" : "Triggers when a new customer is created.", + "help" : null, + "name" : "newCustomer", + "outputDefinition" : { + "output" : null, + "outputResponse" : { + "outputSchema" : { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : null, + "type" : "OBJECT", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "additionalProperties" : null, + "multipleValues" : null, + "options" : null, + "properties" : [ { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "id", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "object", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "description", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "email", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "name", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "phone", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "address", + "type" : "OBJECT", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "additionalProperties" : null, + "multipleValues" : null, + "options" : null, + "properties" : [ { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "city", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "country", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "line1", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "line2", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "postal_code", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "state", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + } ], + "controlType" : "OBJECT_BUILDER", + "optionsDataSource" : null + } ], + "controlType" : "OBJECT_BUILDER", + "optionsDataSource" : null + }, + "sampleOutput" : null + }, + "outputSchema" : { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : null, + "type" : "OBJECT", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "additionalProperties" : null, + "multipleValues" : null, + "options" : null, + "properties" : [ { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "id", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "object", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "description", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "email", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "name", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "phone", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "address", + "type" : "OBJECT", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "additionalProperties" : null, + "multipleValues" : null, + "options" : null, + "properties" : [ { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "city", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "country", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "line1", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "line2", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "postal_code", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "state", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + } ], + "controlType" : "OBJECT_BUILDER", + "optionsDataSource" : null + } ], + "controlType" : "OBJECT_BUILDER", + "optionsDataSource" : null + }, + "sampleOutput" : null + }, + "properties" : null, + "title" : "New Customer", + "type" : "DYNAMIC_WEBHOOK", + "webhookRawBody" : null, + "workflowSyncExecution" : null, + "workflowNodeDescription" : null, + "processErrorResponse" : null, + "deduplicate" : null, + "webhookDisable" : { }, + "webhookEnable" : { }, + "dynamicWebhookRefresh" : null, + "listenerDisable" : null, + "listenerEnable" : null, + "webhookRequest" : { }, + "webhookValidate" : null, + "webhookValidateOnEnable" : null, + "poll" : null + }, { + "batch" : null, + "deprecated" : null, + "description" : "Triggers on a new invoice.", + "help" : null, + "name" : "newInvoice", + "outputDefinition" : { + "output" : null, + "outputResponse" : { + "outputSchema" : { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : null, + "type" : "OBJECT", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "additionalProperties" : null, + "multipleValues" : null, + "options" : null, + "properties" : [ { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "id", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "object", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "currency", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "customer", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "customer_name", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "description", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + } ], + "controlType" : "OBJECT_BUILDER", + "optionsDataSource" : null + }, + "sampleOutput" : null + }, + "outputSchema" : { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : null, + "type" : "OBJECT", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "additionalProperties" : null, + "multipleValues" : null, + "options" : null, + "properties" : [ { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "id", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "object", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "currency", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "customer", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "customer_name", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : null, + "name" : "description", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : null, + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + } ], + "controlType" : "OBJECT_BUILDER", + "optionsDataSource" : null + }, + "sampleOutput" : null + }, + "properties" : null, + "title" : "New Invoice", + "type" : "DYNAMIC_WEBHOOK", + "webhookRawBody" : null, + "workflowSyncExecution" : null, + "workflowNodeDescription" : null, + "processErrorResponse" : null, + "deduplicate" : null, + "webhookDisable" : { }, + "webhookEnable" : { }, + "dynamicWebhookRefresh" : null, + "listenerDisable" : null, + "listenerEnable" : null, + "webhookRequest" : { }, + "webhookValidate" : null, + "webhookValidateOnEnable" : null, + "poll" : null + } ], + "connection" : { + "authorizations" : [ { + "detectOn" : null, + "description" : null, + "name" : "bearer_token", + "properties" : [ { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "expressionEnabled" : null, + "hidden" : null, + "metadata" : { }, + "required" : true, + "name" : "token", + "type" : "STRING", + "defaultValue" : null, + "exampleValue" : null, + "label" : "Token", + "placeholder" : null, + "controlType" : "TEXT", + "languageId" : null, + "maxLength" : null, + "minLength" : null, + "options" : null, + "optionsDataSource" : null + } ], + "refreshOn" : null, + "title" : "Bearer Token", + "type" : "BEARER_TOKEN", + "acquire" : null, + "apply" : { }, + "authorizationCallback" : null, + "authorizationUrl" : null, + "clientId" : null, + "clientSecret" : null, + "oauth2AuthorizationExtraQueryParameters" : null, + "pkce" : null, + "refresh" : null, + "refreshUrl" : null, + "scopes" : null, + "refreshToken" : null, + "tokenUrl" : null + } ], + "properties" : null, + "version" : 1, + "authorizationRequired" : null, + "baseUri" : { }, + "test" : null + } +} \ No newline at end of file diff --git a/server/libs/platform/platform-component/platform-component-service/src/main/java/com/bytechef/platform/component/definition/HttpClientExecutor.java b/server/libs/platform/platform-component/platform-component-service/src/main/java/com/bytechef/platform/component/definition/HttpClientExecutor.java index 7341a78759c..223b7224447 100644 --- a/server/libs/platform/platform-component/platform-component-service/src/main/java/com/bytechef/platform/component/definition/HttpClientExecutor.java +++ b/server/libs/platform/platform-component/platform-component-service/src/main/java/com/bytechef/platform/component/definition/HttpClientExecutor.java @@ -376,15 +376,31 @@ private BodyPublisher getFormUrlEncodedBodyPublisher(Body body) { FormBodyPublisher.Builder builder = FormBodyPublisher.newBuilder(); - for (Map.Entry parameter : bodyParameters.entrySet()) { - Object value = Validate.notNull(parameter.getValue(), "expected value for " + parameter.getKey()); - - builder.query((String) parameter.getKey(), value.toString()); - } + processParameters("", bodyParameters, builder); return builder.build(); } + private void processParameters(String prefix, Map parameters, FormBodyPublisher.Builder builder) { + parameters.forEach((key, value) -> { + Validate.notNull(value, "Expected value for " + key); + + String newKey = prefix.isEmpty() ? key.toString() : prefix + "[" + key + "]"; + + if (value instanceof Map nestedMap) { + processParameters(newKey, nestedMap, builder); + } else if (value instanceof List list) { + for (int i = 0; i < list.size(); i++) { + Object item = list.get(i); + + builder.query(newKey + "[" + i + "]", item.toString()); + } + } else { + builder.query(newKey, value.toString()); + } + }); + } + private Methanol.Interceptor getInterceptor( String componentName, int componentVersion, String componentOperationName, int connectionVersion, String authorizationName, boolean credentialsBeRefreshed, boolean isAction) { diff --git a/server/libs/platform/platform-component/platform-component-service/src/test/java/com/bytechef/platform/component/definition/HttpClientExecutorTest.java b/server/libs/platform/platform-component/platform-component-service/src/test/java/com/bytechef/platform/component/definition/HttpClientExecutorTest.java index 5b0a7ce60d4..58dc5275b71 100644 --- a/server/libs/platform/platform-component/platform-component-service/src/test/java/com/bytechef/platform/component/definition/HttpClientExecutorTest.java +++ b/server/libs/platform/platform-component/platform-component-service/src/test/java/com/bytechef/platform/component/definition/HttpClientExecutorTest.java @@ -16,6 +16,11 @@ package com.bytechef.platform.component.definition; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + import com.bytechef.component.definition.ActionContext; import com.bytechef.component.definition.Authorization; import com.bytechef.component.definition.Context; @@ -35,6 +40,7 @@ import java.io.InputStream; import java.lang.reflect.Type; import java.net.URI; +import java.net.URLEncoder; import java.net.http.HttpClient; import java.net.http.HttpHeaders; import java.net.http.HttpRequest; @@ -47,7 +53,6 @@ import java.util.Map; import java.util.Optional; import javax.net.ssl.SSLSession; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -74,7 +79,7 @@ public void testCreateBodyHandler() { HttpResponse.BodyHandler bodyHandler = httpClientExecutor.createBodyHandler( configuration); - Assertions.assertEquals(bodyHandler, HttpResponse.BodyHandlers.discarding()); + assertEquals(bodyHandler, HttpResponse.BodyHandlers.discarding()); // @@ -82,7 +87,7 @@ public void testCreateBodyHandler() { Http.responseType(Http.ResponseType.BINARY) .build()); - Assertions.assertEquals(bodyHandler, HttpResponse.BodyHandlers.ofInputStream()); + assertEquals(bodyHandler, HttpResponse.BodyHandlers.ofInputStream()); // @@ -90,7 +95,7 @@ public void testCreateBodyHandler() { Http.responseType(Http.ResponseType.XML) .build()); - Assertions.assertEquals(bodyHandler, HttpResponse.BodyHandlers.ofString()); + assertEquals(bodyHandler, HttpResponse.BodyHandlers.ofString()); } @Test @@ -109,7 +114,7 @@ public void testCreateBodyPublisher() { Http.Body.of( Map.of("key1", "value1", "key2", fileEntry), Http.BodyContentType.FORM_DATA)); - Assertions.assertTrue(multipartBodyPublisher.mediaType() + assertTrue(multipartBodyPublisher.mediaType() .toString() .startsWith("multipart/form-data")); @@ -120,45 +125,58 @@ public void testCreateBodyPublisher() { .findFirst() .orElseThrow(); - Assertions.assertEquals(MediaType.TEXT_PLAIN, mimeBodyPublisherAdapter.mediaType()); + assertEquals(MediaType.TEXT_PLAIN, mimeBodyPublisherAdapter.mediaType()); // FormBodyPublisher formBodyPublisher = (FormBodyPublisher) httpClientExecutor.createBodyPublisher( Http.Body.of( - Map.of("key1", "value1", "key2", "value2"), Http.BodyContentType.FORM_URL_ENCODED)); - - Assertions.assertEquals(MediaType.APPLICATION_FORM_URLENCODED, formBodyPublisher.mediaType()); - - Assertions.assertTrue(formBodyPublisher.encodedString() + Map.of( + "key1", "value1", "key2", "value2", + "key3", Map.of( + "key31", "value31", + "key32", Map.of( + "key321", "value321", + "key322", List.of("value3221", "value3222")))), + Http.BodyContentType.FORM_URL_ENCODED)); + + assertEquals(MediaType.APPLICATION_FORM_URLENCODED, formBodyPublisher.mediaType()); + + assertTrue(formBodyPublisher.encodedString() .contains("key1=value1")); - Assertions.assertTrue(formBodyPublisher.encodedString() + assertTrue(formBodyPublisher.encodedString() .contains("key2=value2")); - - // + assertTrue(formBodyPublisher.encodedString() + .contains(URLEncoder.encode("key3[key31]", StandardCharsets.UTF_8) + "=value31")); + assertTrue(formBodyPublisher.encodedString() + .contains(URLEncoder.encode("key3[key32][key321]", StandardCharsets.UTF_8) + "=value321")); + assertTrue(formBodyPublisher.encodedString() + .contains(URLEncoder.encode("key3[key32][key322][0]", StandardCharsets.UTF_8) + "=value3221")); + assertTrue(formBodyPublisher.encodedString() + .contains(URLEncoder.encode("key3[key32][key322][1]", StandardCharsets.UTF_8) + "=value3222")); mimeBodyPublisherAdapter = (MimeBodyPublisherAdapter) httpClientExecutor.createBodyPublisher( Http.Body.of(Map.of("key1", "value1"), Http.BodyContentType.JSON)); - Assertions.assertEquals(MediaType.APPLICATION_JSON, mimeBodyPublisherAdapter.mediaType()); + assertEquals(MediaType.APPLICATION_JSON, mimeBodyPublisherAdapter.mediaType()); // mimeBodyPublisherAdapter = (MimeBodyPublisherAdapter) httpClientExecutor.createBodyPublisher( Http.Body.of(Map.of("key1", "value1"), Http.BodyContentType.XML)); - Assertions.assertEquals(MediaType.APPLICATION_XML, mimeBodyPublisherAdapter.mediaType()); + assertEquals(MediaType.APPLICATION_XML, mimeBodyPublisherAdapter.mediaType()); // mimeBodyPublisherAdapter = (MimeBodyPublisherAdapter) httpClientExecutor.createBodyPublisher( Http.Body.of("text")); - Assertions.assertEquals(MediaType.TEXT_PLAIN, mimeBodyPublisherAdapter.mediaType()); + assertEquals(MediaType.TEXT_PLAIN, mimeBodyPublisherAdapter.mediaType()); HttpRequest.BodyPublisher emptyBodyPublisher = httpClientExecutor.createBodyPublisher(null); - Assertions.assertEquals(0, emptyBodyPublisher.contentLength()); + assertEquals(0, emptyBodyPublisher.contentLength()); // @@ -174,13 +192,13 @@ public void testCreateBodyPublisher() { mimeBodyPublisherAdapter = (MimeBodyPublisherAdapter) httpClientExecutor.createBodyPublisher( Http.Body.of(fileEntry)); - Assertions.assertEquals(MediaType.TEXT_PLAIN, mimeBodyPublisherAdapter.mediaType()); + assertEquals(MediaType.TEXT_PLAIN, mimeBodyPublisherAdapter.mediaType()); // HttpRequest.BodyPublisher bodyPublisher = httpClientExecutor.createBodyPublisher(null); - Assertions.assertEquals(0, bodyPublisher.contentLength()); + assertEquals(0, bodyPublisher.contentLength()); } @Disabled @@ -195,10 +213,10 @@ public void testCreateHTTPClient() { new ComponentConnection("componentName", 1, -1, Map.of(), Authorization.AuthorizationType.NONE.name()), Mockito.mock(Context.class)); - Assertions.assertTrue(httpClient.authenticator() + assertTrue(httpClient.authenticator() .isEmpty()); - Assertions.assertNotNull(httpClient.sslContext()); + assertNotNull(httpClient.sslContext()); // @@ -220,7 +238,7 @@ public void testCreateHTTPClient() { new ComponentConnection("componentName", 1, -1, Map.of(), Authorization.AuthorizationType.NONE.name()), Mockito.mock(Context.class)); - Assertions.assertEquals(Map.of(Authorization.API_TOKEN, List.of("token_value")), headers); + assertEquals(Map.of(Authorization.API_TOKEN, List.of("token_value")), headers); // Mockito.when(context.fetchConnection()) // .thenReturn( @@ -241,7 +259,7 @@ public void testCreateHTTPClient() { new ComponentConnection("componentName", 1, -1, Map.of(), Authorization.AuthorizationType.NONE.name()), Mockito.mock(Context.class)); - Assertions.assertEquals(Map.of(Authorization.API_TOKEN, List.of("token_value")), queryParameters); + assertEquals(Map.of(Authorization.API_TOKEN, List.of("token_value")), queryParameters); // Mockito.when(context.fetchConnection()) // .thenReturn( @@ -259,7 +277,7 @@ public void testCreateHTTPClient() { new ComponentConnection("componentName", 1, -1, Map.of(), Authorization.AuthorizationType.NONE.name()), Mockito.mock(Context.class)); - Assertions.assertEquals( + assertEquals( Map.of( "Authorization", List.of("Basic " + encoder @@ -282,7 +300,7 @@ public void testCreateHTTPClient() { new ComponentConnection("componentName", 1, -1, Map.of(), Authorization.AuthorizationType.NONE.name()), Mockito.mock(Context.class)); - Assertions.assertEquals(Map.of("Authorization", List.of("Bearer token")), headers); + assertEquals(Map.of("Authorization", List.of("Bearer token")), headers); // Mockito.when(context.fetchConnection()) // .thenReturn( @@ -302,7 +320,7 @@ public void testCreateHTTPClient() { new ComponentConnection("componentName", 1, -1, Map.of(), Authorization.AuthorizationType.NONE.name()), Mockito.mock(Context.class)); - Assertions.assertEquals( + assertEquals( Map.of( "Authorization", List.of("Basic " + encoder.encodeToString("username:password".getBytes(StandardCharsets.UTF_8)))), @@ -324,7 +342,7 @@ public void testCreateHTTPClient() { new ComponentConnection("componentName", 1, -1, Map.of(), Authorization.AuthorizationType.NONE.name()), Mockito.mock(Context.class)); - Assertions.assertEquals(Map.of("Authorization", List.of("Bearer access_token")), headers); + assertEquals(Map.of("Authorization", List.of("Bearer access_token")), headers); // @@ -335,7 +353,7 @@ public void testCreateHTTPClient() { new ComponentConnection("componentName", 1, -1, Map.of(), Authorization.AuthorizationType.NONE.name()), Mockito.mock(Context.class)); - Assertions.assertNotNull(httpClient.followRedirects()); + assertNotNull(httpClient.followRedirects()); // @@ -346,7 +364,7 @@ public void testCreateHTTPClient() { new ComponentConnection("componentName", 1, -1, Map.of(), Authorization.AuthorizationType.NONE.name()), Mockito.mock(Context.class)); - Assertions.assertNotNull(httpClient.followRedirects()); + assertNotNull(httpClient.followRedirects()); // @@ -357,7 +375,7 @@ public void testCreateHTTPClient() { new ComponentConnection("componentName", 1, -1, Map.of(), Authorization.AuthorizationType.NONE.name()), Mockito.mock(Context.class)); - Assertions.assertTrue(httpClient.proxy() + assertTrue(httpClient.proxy() .isPresent()); // @@ -370,7 +388,7 @@ public void testCreateHTTPClient() { new ComponentConnection("componentName", 1, -1L, Map.of(), Authorization.AuthorizationType.NONE.name()), Mockito.mock(Context.class)); - Assertions.assertEquals( + assertEquals( Duration.ofMillis(2000), httpClient.connectTimeout() .orElseThrow()); } @@ -383,18 +401,18 @@ public void testCreateHTTPRequest() { new ComponentConnection("componentName", 1, -1L, Map.of(), Authorization.AuthorizationType.NONE.name()), Mockito.mock(Context.class)); - Assertions.assertEquals(Http.RequestMethod.DELETE.name(), httpRequest.method()); + assertEquals(Http.RequestMethod.DELETE.name(), httpRequest.method()); HttpHeaders httpHeaders = httpRequest.headers(); - Assertions.assertEquals(Map.of("header1", List.of("value1")), httpHeaders.map()); - Assertions.assertEquals(URI.create("http://localhost:8080?param1=value1"), httpRequest.uri()); + assertEquals(Map.of("header1", List.of("value1")), httpHeaders.map()); + assertEquals(URI.create("http://localhost:8080?param1=value1"), httpRequest.uri()); } @Disabled @Test public void testHandleResponse() { - Assertions.assertNull( + assertNull( httpClientExecutor .handleResponse(new TestHttpResponse(null), configuration) .getBody()); @@ -407,7 +425,7 @@ public void testHandleResponse() { .when(context.file(file -> file.storeContent(Mockito.anyString(), (InputStream) Mockito.any()))) .thenReturn(fileEntry); - Assertions.assertEquals( + assertEquals( fileEntry, httpClientExecutor .handleResponse( @@ -418,7 +436,7 @@ public void testHandleResponse() { // - Assertions.assertEquals( + assertEquals( Map.of("key1", "value1"), httpClientExecutor .handleResponse( @@ -434,7 +452,7 @@ public void testHandleResponse() { // - Assertions.assertEquals( + assertEquals( "text", httpClientExecutor .handleResponse( @@ -445,7 +463,7 @@ public void testHandleResponse() { // - Assertions.assertEquals( + assertEquals( Map.of("object", Map.of("key1", "value1")), httpClientExecutor .handleResponse( @@ -467,7 +485,7 @@ public void testHandleResponse() { Http.Configuration.ConfigurationBuilder configurationBuilder = Http.responseType(Http.ResponseType.TEXT); - Assertions.assertEquals( + assertEquals( new TestResponseImpl(Map.of(), "text", 200), httpClientExecutor.handleResponse(new TestHttpResponse("text"), configurationBuilder.build())); } @@ -485,7 +503,7 @@ public void testHandleResponseWithIncompatibleResponseType() { Http.Response response = httpClientExecutor.handleResponse(testHttpResponse, configurationBuilder.build()); - Assertions.assertNull(response.getBody()); + assertNull(response.getBody()); testHttpResponse = new TestHttpResponse( "{\"key1\":\"value1\", \"key2\":\"value2\"}", @@ -493,9 +511,9 @@ public void testHandleResponseWithIncompatibleResponseType() { response = httpClientExecutor.handleResponse(testHttpResponse, configurationBuilder.build()); - Assertions.assertNotNull(response.getBody()); + assertNotNull(response.getBody()); - Assertions.assertEquals(Map.of("key1", "value1", "key2", "value2"), response.getBody()); + assertEquals(Map.of("key1", "value1", "key2", "value2"), response.getBody()); testHttpResponse = new TestHttpResponse( "\"value1\"\"value2\"", @@ -503,7 +521,7 @@ public void testHandleResponseWithIncompatibleResponseType() { response = httpClientExecutor.handleResponse(testHttpResponse, configurationBuilder.build()); - Assertions.assertNull(response.getBody()); + assertNull(response.getBody()); testHttpResponse = new TestHttpResponse( "value1value2", @@ -513,9 +531,9 @@ public void testHandleResponseWithIncompatibleResponseType() { testHttpResponse, configurationBuilder.responseType(Http.ResponseType.XML) .build()); - Assertions.assertNotNull(response.getBody()); + assertNotNull(response.getBody()); - Assertions.assertEquals(Map.of("key1", "value1", "key2", "value2"), response.getBody()); + assertEquals(Map.of("key1", "value1", "key2", "value2"), response.getBody()); } private static class TestResponseImpl implements Http.Response { diff --git a/settings.gradle.kts b/settings.gradle.kts index cbfab632195..b756f22189c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -316,6 +316,7 @@ include("server:libs:modules:components:sendgrid") include("server:libs:modules:components:shopify") include("server:libs:modules:components:slack") include("server:libs:modules:components:spotify") +include("server:libs:modules:components:stripe") include("server:libs:modules:components:teamwork") include("server:libs:modules:components:text-helper") include("server:libs:modules:components:todoist")