diff --git a/server/libs/modules/components/liferay/build.gradle.kts b/server/libs/modules/components/liferay/build.gradle.kts index 190b578b89..c1219060de 100644 --- a/server/libs/modules/components/liferay/build.gradle.kts +++ b/server/libs/modules/components/liferay/build.gradle.kts @@ -1 +1,5 @@ version="1.0" + +dependencies { + implementation("com.github.ben-manes.caffeine:caffeine") +} diff --git a/server/libs/modules/components/liferay/src/main/java/com/bytechef/component/liferay/action/LiferayHeadlessAction.java b/server/libs/modules/components/liferay/src/main/java/com/bytechef/component/liferay/action/LiferayHeadlessAction.java index e3e96e4244..a3cdda60b2 100644 --- a/server/libs/modules/components/liferay/src/main/java/com/bytechef/component/liferay/action/LiferayHeadlessAction.java +++ b/server/libs/modules/components/liferay/src/main/java/com/bytechef/component/liferay/action/LiferayHeadlessAction.java @@ -17,51 +17,138 @@ package com.bytechef.component.liferay.action; import static com.bytechef.component.definition.ComponentDsl.action; +import static com.bytechef.component.definition.ComponentDsl.dynamicProperties; import static com.bytechef.component.definition.ComponentDsl.string; import static com.bytechef.component.definition.ConnectionDefinition.BASE_URI; +import static com.bytechef.component.definition.Context.Http.responseType; +import static com.bytechef.component.liferay.constant.LiferayConstants.APPLICATION; +import static com.bytechef.component.liferay.constant.LiferayConstants.ENDPOINT; +import static com.bytechef.component.liferay.constant.LiferayConstants.PROPERTIES; +import com.bytechef.component.definition.ActionContext; import com.bytechef.component.definition.ComponentDsl.ModifiableActionDefinition; import com.bytechef.component.definition.Context; import com.bytechef.component.definition.Context.Http; +import com.bytechef.component.definition.Context.Http.Body; +import com.bytechef.component.definition.Context.Http.Executor; +import com.bytechef.component.definition.Context.Http.Response; +import com.bytechef.component.definition.Context.Http.ResponseType; +import com.bytechef.component.definition.OptionsDataSource.ActionOptionsFunction; import com.bytechef.component.definition.Parameters; +import com.bytechef.component.definition.PropertiesDataSource; +import com.bytechef.component.liferay.util.LiferayOptionUtils; +import com.bytechef.component.liferay.util.LiferayPropertiesUtils; +import com.bytechef.component.liferay.util.PropertiesContainer; import java.time.Duration; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; /** * @author Igor Beslic */ public class LiferayHeadlessAction { - public static final String ENDPOINT = "endpoint"; - - public static final ModifiableActionDefinition ACTION_DEFINITION = action("headless") - .title("Headless Api") + public static final ModifiableActionDefinition ACTION_DEFINITION = action("headlessRequest") + .title("Headless Request") .description("The Headless endpoint to use.") .properties( + string(APPLICATION) + .label("Application") + .options((ActionOptionsFunction) LiferayOptionUtils::getApplicationsOptions) + .required(true), string(ENDPOINT) .label("Endpoint") - .required(true) - .placeholder("headless-portal/v1.0)")) + .options((ActionOptionsFunction) LiferayOptionUtils::getEndpointsOptions) + .optionsLookupDependsOn(APPLICATION) + .required(true), + dynamicProperties(PROPERTIES) + .properties( + (PropertiesDataSource.ActionPropertiesFunction) ( + inputParameters, connectionParameters, lookupDependsOnPaths, context) -> { + + PropertiesContainer propertiesContainer = LiferayPropertiesUtils.createPropertiesForParameters( + inputParameters.getRequiredString(APPLICATION), inputParameters.getRequiredString(ENDPOINT), + context); + + return propertiesContainer.properties(); + }) + .propertiesLookupDependsOn(APPLICATION, ENDPOINT) + .required(false)) .output() .perform(LiferayHeadlessAction::perform); - public static Object perform(Parameters inputParameters, Parameters connectionParameters, Context context) { + public static Object perform(Parameters inputParameters, Parameters connectionParameters, ActionContext context) { String baseUri = connectionParameters.getRequiredString(BASE_URI); String endpoint = inputParameters.getRequiredString(ENDPOINT); - String endpointUri = baseUri + "/" + endpoint; + String[] endpointParts = endpoint.split(" "); + String method = endpointParts[0]; + String endpointUrl = endpointParts[1]; - Http.Response response = context.http(http -> http.get(endpointUri)) - .configuration(Http.timeout(Duration.ofMillis(inputParameters.getInteger("timeout", 10000)))) - .execute(); + String endpointUri = baseUri + "/o/" + inputParameters.getRequiredString(APPLICATION) + endpointUrl; + + Map properties = inputParameters.getMap(PROPERTIES); + + Map body = Map.of(); + Map> headers = Map.of(); + Map> queryParameters = Map.of(); + Map pathParameters = Map.of(); + + PropertiesContainer propertiesContainer = LiferayPropertiesUtils.createPropertiesForParameters( + inputParameters.getRequiredString(APPLICATION), inputParameters.getRequiredString(ENDPOINT), + context); + + if (properties != null) { + body = propertiesContainer.bodyParameters() + .stream() + .filter(properties::containsKey) + .collect(Collectors.toMap(p -> p, p -> String.valueOf(properties.get(p)))); - int statusCode = response.getStatusCode(); + headers = propertiesContainer.headerParameters() + .stream() + .filter(properties::containsKey) + .collect(Collectors.toMap(p -> p, p -> List.of(String.valueOf(properties.get(p))))); - if ((statusCode >= 200) && (statusCode < 300)) { - return response.getBody(); + queryParameters = propertiesContainer.queryParameters() + .stream() + .filter(properties::containsKey) + .collect(Collectors.toMap(p -> p, p -> List.of(String.valueOf(properties.get(p))))); + + pathParameters = propertiesContainer.headerParameters() + .stream() + .filter(properties::containsKey) + .collect(Collectors.toMap(p -> p, p -> String.valueOf(properties.get(p)))); } - context.log(log -> log.warn("Received response code {}, from endpoint {}", statusCode, endpointUri)); + for (Map.Entry entry : pathParameters.entrySet()) { + String key = entry.getKey(); + String value = String.valueOf(entry.getValue()); + + endpointUri = endpointUri.replace("{" + key + "}", value); + } + + Executor executor = getExecutor(context, method, endpointUri); + + Response response = executor.headers(headers) + .queryParameters(queryParameters) + .configuration(Http.timeout(Duration.ofMillis(inputParameters.getInteger("timeout", 10000)))) + .configuration(responseType(ResponseType.JSON)) + .body(Body.of(body)) + .execute(); + + return response.getBody(); + } - return endpoint; + private static Executor getExecutor(Context context, String method, String finalEndpointUri) { + return context.http(http -> switch (method) { + case "GET" -> http.get(finalEndpointUri); + case "POST" -> http.post(finalEndpointUri); + case "PUT" -> http.put(finalEndpointUri); + case "PATCH" -> http.patch(finalEndpointUri); + case "DELETE" -> http.delete(finalEndpointUri); + case "HEAD" -> http.head(finalEndpointUri); + default -> throw new IllegalArgumentException("Unknown HTTP method: " + method); + }); } } diff --git a/server/libs/modules/components/liferay/src/main/java/com/bytechef/component/liferay/constant/LiferayConstants.java b/server/libs/modules/components/liferay/src/main/java/com/bytechef/component/liferay/constant/LiferayConstants.java index 0c54cd3f9c..0751705768 100644 --- a/server/libs/modules/components/liferay/src/main/java/com/bytechef/component/liferay/constant/LiferayConstants.java +++ b/server/libs/modules/components/liferay/src/main/java/com/bytechef/component/liferay/constant/LiferayConstants.java @@ -27,14 +27,16 @@ */ public class LiferayConstants { + public static final String APPLICATION = "application"; public static final String CONTEXT_NAME = "contextName"; public static final String DISCOVER = "discover"; public static final String ENDPOINT = "endpoint"; public static final String GET = "GET"; - public static final String NAME = "name"; public static final String METHOD = "method"; + public static final String NAME = "name"; public static final String PARAMETERS = "parameters"; public static final String POST = "POST"; + public static final String PROPERTIES = "properties"; public static final String SERVICE = "service"; public static final String SERVICES = "services"; public static final String TYPE = "type"; @@ -43,4 +45,5 @@ public class LiferayConstants { .propertiesLookupDependsOn(SERVICE) .properties((ActionPropertiesFunction) LiferayUtils::createParameters) .required(true); + } diff --git a/server/libs/modules/components/liferay/src/main/java/com/bytechef/component/liferay/util/LiferayOptionUtils.java b/server/libs/modules/components/liferay/src/main/java/com/bytechef/component/liferay/util/LiferayOptionUtils.java new file mode 100644 index 0000000000..33f7e85728 --- /dev/null +++ b/server/libs/modules/components/liferay/src/main/java/com/bytechef/component/liferay/util/LiferayOptionUtils.java @@ -0,0 +1,113 @@ +/* + * Copyright 2025 ByteChef + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.bytechef.component.liferay.util; + +import static com.bytechef.component.definition.ComponentDsl.option; +import static com.bytechef.component.definition.Context.Http.responseType; +import static com.bytechef.component.liferay.constant.LiferayConstants.APPLICATION; + +import com.bytechef.component.definition.Context; +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.TypeReference; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * @author Marija Horvat + */ +public class LiferayOptionUtils { + + private LiferayOptionUtils() { + } + + public static List> getApplicationsOptions( + Parameters inputParameters, Parameters connectionParameters, Map lookupDependsOnPaths, + String searchText, Context context) { + + List> options = new ArrayList<>(); + + Map body = context + .http(http -> http.get("/o/openapi/openapi.json")) + .configuration(responseType(ResponseType.JSON)) + .execute() + .getBody(new TypeReference<>() {}); + + if (!(body.get("paths") instanceof Map resources)) { + return List.of(); + } + + List uniqueApps = resources.keySet() + .stream() + .map(Object::toString) + .filter(path -> path.contains("openapi.{type")) + .map(path -> path.replaceFirst("/", "")) + .map(path -> path.replaceFirst("/openapi\\.\\{type.*}$", "")) + .distinct() + .toList(); + + for (String app : uniqueApps) { + options.add(option(app, app)); + } + + return options; + } + + public static List> getEndpointsOptions( + Parameters inputParameters, Parameters connectionParameters, Map lookupDependsOnPaths, + String searchText, Context context) { + + List> options = new ArrayList<>(); + + Map body = context + .http(http -> http.get("/o/" + inputParameters.getRequiredString(APPLICATION) + "/openapi.json")) + .configuration(responseType(ResponseType.JSON)) + .execute() + .getBody(new TypeReference<>() {}); + + String version = ""; + + if (body.get("info") instanceof Map info) { + version = String.valueOf(info.get("version")); + } + + if (body.get("paths") instanceof Map paths) { + for (Map.Entry pathEntry : paths.entrySet()) { + String endpoint = String.valueOf(pathEntry.getKey()); + Object methodsObj = pathEntry.getValue(); + + if (methodsObj instanceof Map methods) { + for (Map.Entry methodEntry : methods.entrySet()) { + String key = String.valueOf(methodEntry.getKey()); + + String method = key.toUpperCase(); + + String label = method + " " + endpoint; + + String value = method + " " + endpoint.replaceFirst("/" + version, ""); + + options.add(option(label, value)); + } + } + } + } + + return options; + } +} diff --git a/server/libs/modules/components/liferay/src/main/java/com/bytechef/component/liferay/util/LiferayPropertiesUtils.java b/server/libs/modules/components/liferay/src/main/java/com/bytechef/component/liferay/util/LiferayPropertiesUtils.java new file mode 100644 index 0000000000..e4756a25f9 --- /dev/null +++ b/server/libs/modules/components/liferay/src/main/java/com/bytechef/component/liferay/util/LiferayPropertiesUtils.java @@ -0,0 +1,274 @@ +/* + * Copyright 2025 ByteChef + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.bytechef.component.liferay.util; + +import static com.bytechef.component.definition.ComponentDsl.array; +import static com.bytechef.component.definition.ComponentDsl.bool; +import static com.bytechef.component.definition.ComponentDsl.integer; +import static com.bytechef.component.definition.ComponentDsl.number; +import static com.bytechef.component.definition.ComponentDsl.object; +import static com.bytechef.component.definition.ComponentDsl.string; +import static com.bytechef.component.definition.Context.Http.responseType; + +import com.bytechef.component.definition.ActionContext; +import com.bytechef.component.definition.ComponentDsl.ModifiableValueProperty; +import com.bytechef.component.definition.Context.Http.ResponseType; +import com.bytechef.component.definition.TypeReference; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +/** + * @author Marija Horvat + */ +public class LiferayPropertiesUtils { + + private static final Cache PROPERTIES_CONTAINER_CACHE = + Caffeine.newBuilder() + .expireAfterAccess(10, TimeUnit.MINUTES) + .maximumSize(1000) + .build(); + + private LiferayPropertiesUtils() { + } + + public static PropertiesContainer createPropertiesForParameters( + String application, String endpoint, ActionContext context) { + + String url = "/o/" + application + "/openapi.json"; + + return PROPERTIES_CONTAINER_CACHE.get(url, cacheKey -> getPropertiesContainer(url, endpoint, context)); + } + + private static List> extractPropertiesFromSchema( + Map schema, Map specification) { + + List> result = new ArrayList<>(); + + if (schema.containsKey("$ref")) { + String ref = (String) schema.get("$ref"); + + String component = ref.replace("#/components/schemas/", ""); + + if (specification.get("components") instanceof Map components && + components.get("schemas") instanceof Map schemas && + schemas.get(component) instanceof Map referencedSchema) { + + return extractPropertiesFromSchema(referencedSchema, specification); + } + } + + if (!(schema.get("properties") instanceof Map props)) { + return result; + } + + for (Map.Entry entry : props.entrySet()) { + String key = String.valueOf(entry.getKey()); + Object value = entry.getValue(); + + if (!(value instanceof Map propertySchema)) { + continue; + } + + Object readOnly = propertySchema.get("readOnly"); + + if (Boolean.TRUE.equals(readOnly)) { + continue; + } + + String type = (String) propertySchema.get("type"); + + if ("object".equals(type) && propertySchema.containsKey("properties")) { + result.addAll(extractPropertiesFromSchema(propertySchema, specification)); + + continue; + } + + if (propertySchema.containsKey("$ref")) { + String ref = (String) propertySchema.get("$ref"); + + String component = ref.replace("#/components/schemas/", ""); + + if (specification.get("components") instanceof Map components && + components.get("schemas") instanceof Map schemas && + schemas.get(component) instanceof Map nestedSchema) { + + result.addAll(extractPropertiesFromSchema(nestedSchema, specification)); + + continue; + } + } + + Map field = new HashMap<>(); + + field.put("name", key); + field.put("schema", Map.of("type", type != null ? type : "string")); + field.put("in", "body"); + field.put("required", true); + + result.add(field); + } + + return result; + } + + private static ModifiableValueProperty createProperty( + Map parameterMap, List bodyParameters, List headerParameters, + List pathParameters, List queryParameters) { + + String in = (String) parameterMap.get("in"); + + if (in == null) { + return null; + } + + String name = (String) parameterMap.get("name"); + + if (name == null) { + return null; + } + + switch (in) { + case "body" -> bodyParameters.add(name); + case "header" -> headerParameters.add(name); + case "path" -> pathParameters.add(name); + case "query" -> queryParameters.add(name); + default -> throw new IllegalArgumentException("Unknown parameter type: " + in); + } + + Object schemaObj = parameterMap.get("schema"); + + if (!(schemaObj instanceof Map schema)) { + return null; + } + + String type = (String) schema.get("type"); + + if (type == null) { + return null; + } + + boolean required = parameterMap.get("required") != null; + + return switch (type) { + case "boolean" -> bool(name) + .label(name) + .required(required); + case "string" -> string(name) + .label(name) + .required(required); + case "number" -> number(name) + .label(name) + .required(required); + case "integer" -> integer(name) + .label(name) + .required(required); + case "object" -> object(name) + .label(name) + .required(required); + case "array" -> array(name) + .label(name) + .items( + string() + .options()) + .required(required); + default -> null; + }; + } + + private static PropertiesContainer getPropertiesContainer(String url, String endpoint, ActionContext context) { + Map body = context.http(http -> http.get(url)) + .configuration(responseType(ResponseType.JSON)) + .execute() + .getBody(new TypeReference<>() {}); + + String[] endpointParts = endpoint.split(" "); + + String method = endpointParts[0].toLowerCase(); + String endpointUrl = endpointParts[1]; + + List> parameters = new ArrayList<>(); + + String version = ""; + + if (body.get("info") instanceof Map info) { + version = String.valueOf(info.get("version")); + } + + if (body.get("paths") instanceof Map paths) { + for (Map.Entry pathEntry : paths.entrySet()) { + String pathKey = String.valueOf(pathEntry.getKey()); + + String replaceFirst = pathKey.replaceFirst("/" + version, ""); + + if (replaceFirst.equals(endpointUrl)) { + Object path = pathEntry.getValue(); + + if (path instanceof Map pathObj) { + Object methodObj = pathObj.get(method); + + if (methodObj instanceof Map methods) { + if (methods.get("parameters") instanceof List parametersList) { + for (Object parametersObj : parametersList) { + if (parametersObj instanceof Map parametersMap) { + Map parametersTypedMap = new HashMap<>(); + + parametersMap.forEach( + (key, value) -> parametersTypedMap.put(String.valueOf(key), value)); + + parameters.add(parametersTypedMap); + } + } + } + + if (methods.get("requestBody") instanceof Map requestBody && + requestBody.get("content") instanceof Map content) { + + Object jsonContent = content.get("application/json"); + + if (jsonContent instanceof Map jsonContentMap && + jsonContentMap.get("schema") instanceof Map schema) { + + parameters.addAll(extractPropertiesFromSchema(schema, body)); + } + } + } + } + } + } + } + + List bodyParameters = new ArrayList<>(); + List headerParameters = new ArrayList<>(); + List pathParameters = new ArrayList<>(); + List queryParameters = new ArrayList<>(); + + return new PropertiesContainer( + new ArrayList<>( + parameters.stream() + .map(parameterMap -> createProperty( + parameterMap, bodyParameters, headerParameters, pathParameters, queryParameters)) + .filter(Objects::nonNull) + .toList()), + bodyParameters, headerParameters, pathParameters, queryParameters); + } +} diff --git a/server/libs/modules/components/liferay/src/main/java/com/bytechef/component/liferay/util/PropertiesContainer.java b/server/libs/modules/components/liferay/src/main/java/com/bytechef/component/liferay/util/PropertiesContainer.java new file mode 100644 index 0000000000..8126168b40 --- /dev/null +++ b/server/libs/modules/components/liferay/src/main/java/com/bytechef/component/liferay/util/PropertiesContainer.java @@ -0,0 +1,28 @@ +/* + * Copyright 2025 ByteChef + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.bytechef.component.liferay.util; + +import com.bytechef.component.definition.Property; +import java.util.List; + +/** + * @author Ivica Cardic + */ +public record PropertiesContainer( + List> properties, List bodyParameters, List headerParameters, + List pathParameters, List queryParameters) { +} diff --git a/server/libs/modules/components/liferay/src/test/java/com/bytechef/component/liferay/LiferayComponentHandlerTest.java b/server/libs/modules/components/liferay/src/test/java/com/bytechef/component/liferay/LiferayComponentHandlerTest.java index 4f30856682..257a2909bd 100644 --- a/server/libs/modules/components/liferay/src/test/java/com/bytechef/component/liferay/LiferayComponentHandlerTest.java +++ b/server/libs/modules/components/liferay/src/test/java/com/bytechef/component/liferay/LiferayComponentHandlerTest.java @@ -26,59 +26,3 @@ void testGetComponentDefinition() { JsonFileAssert.assertEquals("definition/liferay_v1.json", new LiferayComponentHandler().getDefinition()); } } - -//void testPerform() { -// when(mockedContext.http(any())) -// .thenReturn(mockedExecutor); -// when(mockedExecutor.configuration(any())) -// .thenReturn(mockedExecutor); -// when(mockedExecutor.header(stringArgumentCaptor.capture(), stringArgumentCaptor.capture())) -// .thenReturn(mockedExecutor); -// when(mockedExecutor.body(bodyArgumentCaptor.capture())) -// .thenReturn(mockedExecutor); -// when(mockedExecutor.execute()) -// .thenReturn(mockedResponse); -// when(mockedResponse.getBody(any(TypeReference.class))) -// .thenReturn(Map.of()); -// -// try (MockedStatic mockedGoogleMapsUtils = mockStatic(GoogleMapsUtils.class)) { -// -// mockedGoogleMapsUtils -// .when(() -> GoogleMapsUtils.getAddressLatLng(contextArgumentCaptor.capture(), -// stringArgumentCaptor.capture())) -// .thenReturn(Map.of(LATITUDE, 0.0, LONGITUDE, 0.0)); -// -// Map result = GoogleMapsGetRouteAction.perform( -// mockedParameters, mockedParameters, mockedContext); -// -// assertEquals(Map.of(), result); -// -// assertEquals(mockedContext, contextArgumentCaptor.getValue()); -// assertEquals(List.of("origin", "destination", "X-Goog-FieldMask", "*"), -// stringArgumentCaptor.getAllValues()); -// -// Body body = bodyArgumentCaptor.getValue(); -// -// Map expectedBody = Map.of( -// ORIGIN, Map.of( -// LOCATION, Map.of( -// LAT_LNG, Map.of( -// LATITUDE, 0.0, -// LONGITUDE, 0.0))), -// DESTINATION, Map.of( -// LOCATION, Map.of( -// LAT_LNG, Map.of( -// LATITUDE, 0.0, -// LONGITUDE, 0.0))), -// TRAVEL_MODE, "DRIVE", -// ROUTING_PREFERENCE, "TRAFFIC_UNAWARE", -// COMPUTE_ALT_ROUTES, false, -// "routeModifiers", Map.of( -// AVOID_TOLLS, false, -// AVOID_HIGHWAYS, false, -// AVOID_FERRIES, false), -// UNITS, "METRIC"); -// -// assertEquals(expectedBody, body.getContent()); -// } -//} diff --git a/server/libs/modules/components/liferay/src/test/resources/definition/liferay_v1.json b/server/libs/modules/components/liferay/src/test/resources/definition/liferay_v1.json index 34522911a0..c02a1a0e5e 100644 --- a/server/libs/modules/components/liferay/src/test/resources/definition/liferay_v1.json +++ b/server/libs/modules/components/liferay/src/test/resources/definition/liferay_v1.json @@ -5,7 +5,7 @@ "description" : "The Headless endpoint to use.", "help" : null, "metadata" : null, - "name" : "headless", + "name" : "headlessRequest", "outputDefinition" : { "output" : null, "outputResponse" : null, @@ -16,7 +16,32 @@ "processErrorResponse" : null, "properties" : [ { "advancedOption" : null, - "controlType" : "TEXT", + "controlType" : "SELECT", + "defaultValue" : null, + "description" : null, + "displayCondition" : null, + "exampleValue" : null, + "expressionEnabled" : null, + "hidden" : null, + "label" : "Application", + "languageId" : null, + "maxLength" : null, + "metadata" : { }, + "minLength" : null, + "name" : "application", + "options" : null, + "optionsDataSource" : { + "options" : { }, + "optionsLookupDependsOn" : null + }, + "optionsLoadedDynamically" : null, + "placeholder" : null, + "regex" : null, + "required" : true, + "type" : "STRING" + }, { + "advancedOption" : null, + "controlType" : "SELECT", "defaultValue" : null, "description" : null, "displayCondition" : null, @@ -30,14 +55,32 @@ "minLength" : null, "name" : "endpoint", "options" : null, - "optionsDataSource" : null, + "optionsDataSource" : { + "options" : { }, + "optionsLookupDependsOn" : [ "application" ] + }, "optionsLoadedDynamically" : null, - "placeholder" : "headless-portal/v1.0)", + "placeholder" : null, "regex" : null, "required" : true, "type" : "STRING" + }, { + "advancedOption" : null, + "description" : null, + "displayCondition" : null, + "dynamicPropertiesDataSource" : { + "properties" : { }, + "propertiesLookupDependsOn" : [ "application", "endpoint" ] + }, + "expressionEnabled" : null, + "header" : null, + "hidden" : null, + "metadata" : { }, + "name" : "properties", + "required" : false, + "type" : "DYNAMIC_PROPERTIES" } ], - "title" : "Headless Api", + "title" : "Headless Request", "workflowNodeDescription" : null }, { "batch" : null,