feat: add support for listing function versions and aliases in AWS Lambda Plugin (#41263)
This commit is contained in:
parent
b79b160d2f
commit
180af275b5
|
|
@ -9,7 +9,11 @@ import com.amazonaws.services.lambda.model.AWSLambdaException;
|
||||||
import com.amazonaws.services.lambda.model.FunctionConfiguration;
|
import com.amazonaws.services.lambda.model.FunctionConfiguration;
|
||||||
import com.amazonaws.services.lambda.model.InvokeRequest;
|
import com.amazonaws.services.lambda.model.InvokeRequest;
|
||||||
import com.amazonaws.services.lambda.model.InvokeResult;
|
import com.amazonaws.services.lambda.model.InvokeResult;
|
||||||
|
import com.amazonaws.services.lambda.model.ListAliasesRequest;
|
||||||
|
import com.amazonaws.services.lambda.model.ListAliasesResult;
|
||||||
import com.amazonaws.services.lambda.model.ListFunctionsResult;
|
import com.amazonaws.services.lambda.model.ListFunctionsResult;
|
||||||
|
import com.amazonaws.services.lambda.model.ListVersionsByFunctionRequest;
|
||||||
|
import com.amazonaws.services.lambda.model.ListVersionsByFunctionResult;
|
||||||
import com.amazonaws.services.lambda.model.ResourceNotFoundException;
|
import com.amazonaws.services.lambda.model.ResourceNotFoundException;
|
||||||
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError;
|
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError;
|
||||||
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException;
|
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException;
|
||||||
|
|
@ -70,6 +74,10 @@ public class AwsLambdaPlugin extends BasePlugin {
|
||||||
ActionExecutionResult result;
|
ActionExecutionResult result;
|
||||||
switch (Objects.requireNonNull(command)) {
|
switch (Objects.requireNonNull(command)) {
|
||||||
case "LIST_FUNCTIONS" -> result = listFunctions(actionConfiguration, connection);
|
case "LIST_FUNCTIONS" -> result = listFunctions(actionConfiguration, connection);
|
||||||
|
case "LIST_FUNCTION_VERSIONS" -> result =
|
||||||
|
listFunctionVersions(actionConfiguration, connection);
|
||||||
|
case "LIST_FUNCTION_ALIASES" -> result =
|
||||||
|
listFunctionAliases(actionConfiguration, connection);
|
||||||
case "INVOKE_FUNCTION" -> result = invokeFunction(actionConfiguration, connection);
|
case "INVOKE_FUNCTION" -> result = invokeFunction(actionConfiguration, connection);
|
||||||
default -> throw new IllegalStateException("Unexpected value: " + command);
|
default -> throw new IllegalStateException("Unexpected value: " + command);
|
||||||
}
|
}
|
||||||
|
|
@ -98,24 +106,108 @@ public class AwsLambdaPlugin extends BasePlugin {
|
||||||
throw new AppsmithPluginException(
|
throw new AppsmithPluginException(
|
||||||
AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR, "request type is missing");
|
AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR, "request type is missing");
|
||||||
}
|
}
|
||||||
ActionExecutionResult actionExecutionResult = listFunctions(null, connection);
|
|
||||||
ArrayNode body = (ArrayNode) actionExecutionResult.getBody();
|
String requestType = request.getRequestType();
|
||||||
List<Map<String, String>> functionNames = StreamSupport.stream(body.spliterator(), false)
|
ActionExecutionResult actionExecutionResult;
|
||||||
.map(function -> function.get("functionName").asText())
|
List<Map<String, String>> options;
|
||||||
.sorted()
|
Map<?, Object> params = request.getParameters() == null ? Map.of() : request.getParameters();
|
||||||
.map(functionName -> Map.of("label", functionName, "value", functionName))
|
|
||||||
.collect(Collectors.toList());
|
switch (requestType) {
|
||||||
|
case "FUNCTION_NAMES" -> {
|
||||||
|
actionExecutionResult = listFunctions(null, connection);
|
||||||
|
ArrayNode body = (ArrayNode) actionExecutionResult.getBody();
|
||||||
|
options = StreamSupport.stream(body.spliterator(), false)
|
||||||
|
.map(function -> function.get("functionName").asText())
|
||||||
|
.sorted()
|
||||||
|
.map(functionName -> Map.of("label", functionName, "value", functionName))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
case "FUNCTION_VERSIONS" -> {
|
||||||
|
// Handle both old and new parameter structures
|
||||||
|
String functionName;
|
||||||
|
if (params.containsKey("parameters") && params.get("parameters") instanceof Map) {
|
||||||
|
Map<?, ?> parameters = (Map<?, ?>) params.get("parameters");
|
||||||
|
functionName = (String) parameters.get("functionName");
|
||||||
|
} else {
|
||||||
|
functionName = (String) params.get("functionName");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!StringUtils.hasText(functionName)) {
|
||||||
|
throw new AppsmithPluginException(
|
||||||
|
AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR,
|
||||||
|
"function name is required for listing versions");
|
||||||
|
}
|
||||||
|
actionExecutionResult = listFunctionVersions(null, connection, functionName);
|
||||||
|
ArrayNode body = (ArrayNode) actionExecutionResult.getBody();
|
||||||
|
options = StreamSupport.stream(body.spliterator(), false)
|
||||||
|
.map(version -> version.get("version").asText())
|
||||||
|
.sorted()
|
||||||
|
.map(version -> Map.of("label", version, "value", version))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
case "FUNCTION_ALIASES" -> {
|
||||||
|
// Handle both old and new parameter structures
|
||||||
|
String functionName;
|
||||||
|
if (params.containsKey("parameters") && params.get("parameters") instanceof Map) {
|
||||||
|
Map<?, ?> parameters = (Map<?, ?>) params.get("parameters");
|
||||||
|
functionName = (String) parameters.get("functionName");
|
||||||
|
} else {
|
||||||
|
functionName = (String) params.get("functionName");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!StringUtils.hasText(functionName)) {
|
||||||
|
throw new AppsmithPluginException(
|
||||||
|
AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR,
|
||||||
|
"function name is required for listing aliases");
|
||||||
|
}
|
||||||
|
actionExecutionResult = listFunctionAliases(null, connection, functionName);
|
||||||
|
ArrayNode body = (ArrayNode) actionExecutionResult.getBody();
|
||||||
|
options = StreamSupport.stream(body.spliterator(), false)
|
||||||
|
.map(alias -> alias.get("name").asText())
|
||||||
|
.sorted()
|
||||||
|
.map(alias -> Map.of("label", alias, "value", alias))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
default -> throw new AppsmithPluginException(
|
||||||
|
AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR, "Unsupported request type: " + requestType);
|
||||||
|
}
|
||||||
|
|
||||||
TriggerResultDTO triggerResultDTO = new TriggerResultDTO();
|
TriggerResultDTO triggerResultDTO = new TriggerResultDTO();
|
||||||
triggerResultDTO.setTrigger(functionNames);
|
triggerResultDTO.setTrigger(options);
|
||||||
|
|
||||||
return Mono.just(triggerResultDTO);
|
return Mono.just(triggerResultDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
ActionExecutionResult invokeFunction(ActionConfiguration actionConfiguration, AWSLambda connection) {
|
ActionExecutionResult invokeFunction(ActionConfiguration actionConfiguration, AWSLambda connection) {
|
||||||
InvokeRequest invokeRequest = new InvokeRequest();
|
InvokeRequest invokeRequest = new InvokeRequest();
|
||||||
invokeRequest.setFunctionName(
|
|
||||||
getDataValueSafelyFromFormData(actionConfiguration.getFormData(), "functionName", STRING_TYPE));
|
// Validate and set function name (required parameter)
|
||||||
|
String functionName =
|
||||||
|
getDataValueSafelyFromFormData(actionConfiguration.getFormData(), "functionName", STRING_TYPE);
|
||||||
|
if (!StringUtils.hasText(functionName)) {
|
||||||
|
throw new AppsmithPluginException(
|
||||||
|
AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR,
|
||||||
|
"Function name is required for Lambda invocation");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get version and alias parameters
|
||||||
|
String functionVersion =
|
||||||
|
getDataValueSafelyFromFormData(actionConfiguration.getFormData(), "functionVersion", STRING_TYPE);
|
||||||
|
String functionAlias =
|
||||||
|
getDataValueSafelyFromFormData(actionConfiguration.getFormData(), "functionAlias", STRING_TYPE);
|
||||||
|
|
||||||
|
// Set function name (without qualifier)
|
||||||
|
invokeRequest.setFunctionName(functionName);
|
||||||
|
|
||||||
|
// Use setQualifier for version/alias instead of embedding in function name
|
||||||
|
if (StringUtils.hasText(functionAlias)) {
|
||||||
|
// If alias is specified, use it (alias takes precedence over version)
|
||||||
|
invokeRequest.setQualifier(functionAlias);
|
||||||
|
} else if (StringUtils.hasText(functionVersion)) {
|
||||||
|
// If version is specified and no alias, use version
|
||||||
|
invokeRequest.setQualifier(functionVersion);
|
||||||
|
}
|
||||||
|
// If neither version nor alias is specified, defaults to $LATEST
|
||||||
invokeRequest.setPayload(
|
invokeRequest.setPayload(
|
||||||
getDataValueSafelyFromFormData(actionConfiguration.getFormData(), "body", STRING_TYPE));
|
getDataValueSafelyFromFormData(actionConfiguration.getFormData(), "body", STRING_TYPE));
|
||||||
invokeRequest.setInvocationType(
|
invokeRequest.setInvocationType(
|
||||||
|
|
@ -145,6 +237,61 @@ public class AwsLambdaPlugin extends BasePlugin {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ActionExecutionResult listFunctionVersions(
|
||||||
|
ActionConfiguration actionConfiguration, AWSLambda connection, String functionName) {
|
||||||
|
if (actionConfiguration != null) {
|
||||||
|
functionName =
|
||||||
|
getDataValueSafelyFromFormData(actionConfiguration.getFormData(), "functionName", STRING_TYPE);
|
||||||
|
}
|
||||||
|
if (!StringUtils.hasText(functionName)) {
|
||||||
|
throw new AppsmithPluginException(
|
||||||
|
AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR,
|
||||||
|
"function name is required for listing versions");
|
||||||
|
}
|
||||||
|
|
||||||
|
ListVersionsByFunctionRequest request = new ListVersionsByFunctionRequest();
|
||||||
|
request.setFunctionName(functionName);
|
||||||
|
|
||||||
|
ListVersionsByFunctionResult listVersionsResult = connection.listVersionsByFunction(request);
|
||||||
|
List<FunctionConfiguration> versions = listVersionsResult.getVersions();
|
||||||
|
|
||||||
|
ActionExecutionResult result = new ActionExecutionResult();
|
||||||
|
result.setBody(objectMapper.valueToTree(versions));
|
||||||
|
result.setIsExecutionSuccess(true);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionExecutionResult listFunctionVersions(ActionConfiguration actionConfiguration, AWSLambda connection) {
|
||||||
|
String functionName =
|
||||||
|
getDataValueSafelyFromFormData(actionConfiguration.getFormData(), "functionName", STRING_TYPE);
|
||||||
|
return listFunctionVersions(null, connection, functionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionExecutionResult listFunctionAliases(
|
||||||
|
ActionConfiguration actionConfiguration, AWSLambda connection, String functionName) {
|
||||||
|
if (actionConfiguration != null) {
|
||||||
|
functionName =
|
||||||
|
getDataValueSafelyFromFormData(actionConfiguration.getFormData(), "functionName", STRING_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
ListAliasesRequest request = new ListAliasesRequest();
|
||||||
|
request.setFunctionName(functionName);
|
||||||
|
|
||||||
|
ListAliasesResult listAliasesResult = connection.listAliases(request);
|
||||||
|
List<com.amazonaws.services.lambda.model.AliasConfiguration> aliases = listAliasesResult.getAliases();
|
||||||
|
|
||||||
|
ActionExecutionResult result = new ActionExecutionResult();
|
||||||
|
result.setBody(objectMapper.valueToTree(aliases));
|
||||||
|
result.setIsExecutionSuccess(true);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionExecutionResult listFunctionAliases(ActionConfiguration actionConfiguration, AWSLambda connection) {
|
||||||
|
String functionName =
|
||||||
|
getDataValueSafelyFromFormData(actionConfiguration.getFormData(), "functionName", STRING_TYPE);
|
||||||
|
return listFunctionAliases(null, connection, functionName);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<AWSLambda> datasourceCreate(DatasourceConfiguration datasourceConfiguration) {
|
public Mono<AWSLambda> datasourceCreate(DatasourceConfiguration datasourceConfiguration) {
|
||||||
log.debug(Thread.currentThread().getName() + ": datasourceCreate() called for AWS Lambda plugin.");
|
log.debug(Thread.currentThread().getName() + ": datasourceCreate() called for AWS Lambda plugin.");
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,66 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "Function version",
|
||||||
|
"tooltipText": "Optional: Specify a version number (e.g., 1, 2, $LATEST) or leave empty for $LATEST.",
|
||||||
|
"subtitle": "",
|
||||||
|
"isRequired": false,
|
||||||
|
"propertyName": "function_version",
|
||||||
|
"configProperty": "actionConfiguration.formData.functionVersion.data",
|
||||||
|
"controlType": "DROP_DOWN",
|
||||||
|
"initialValue": "",
|
||||||
|
"options": [],
|
||||||
|
"placeholderText": "Leave empty for $LATEST version",
|
||||||
|
"fetchOptionsConditionally": true,
|
||||||
|
"setFirstOptionAsDefault": false,
|
||||||
|
"alternateViewTypes": ["json"],
|
||||||
|
"conditionals": {
|
||||||
|
"enable": "{{actionConfiguration.formData.functionName.data}}",
|
||||||
|
"fetchDynamicValues": {
|
||||||
|
"condition": "{{actionConfiguration.formData.command.data === 'INVOKE_FUNCTION' && actionConfiguration.formData.functionName.data}}",
|
||||||
|
"config": {
|
||||||
|
"params": {
|
||||||
|
"requestType": "FUNCTION_VERSIONS",
|
||||||
|
"displayType": "DROP_DOWN",
|
||||||
|
"parameters": {
|
||||||
|
"functionName": "{{actionConfiguration.formData.functionName.data}}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Function alias",
|
||||||
|
"tooltipText": "Optional: Specify an alias name (e.g., PROD, STAGING) or leave empty for no alias.",
|
||||||
|
"subtitle": "",
|
||||||
|
"isRequired": false,
|
||||||
|
"propertyName": "function_alias",
|
||||||
|
"configProperty": "actionConfiguration.formData.functionAlias.data",
|
||||||
|
"controlType": "DROP_DOWN",
|
||||||
|
"initialValue": "",
|
||||||
|
"options": [],
|
||||||
|
"placeholderText": "Leave empty for no alias",
|
||||||
|
"fetchOptionsConditionally": true,
|
||||||
|
"setFirstOptionAsDefault": false,
|
||||||
|
"alternateViewTypes": ["json"],
|
||||||
|
"conditionals": {
|
||||||
|
"enable": "{{actionConfiguration.formData.functionName.data}}",
|
||||||
|
"fetchDynamicValues": {
|
||||||
|
"condition": "{{actionConfiguration.formData.command.data === 'INVOKE_FUNCTION' && actionConfiguration.formData.functionName.data}}",
|
||||||
|
"config": {
|
||||||
|
"params": {
|
||||||
|
"requestType": "FUNCTION_ALIASES",
|
||||||
|
"displayType": "DROP_DOWN",
|
||||||
|
"parameters": {
|
||||||
|
"functionName": "{{actionConfiguration.formData.functionName.data}}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "Type of invocation",
|
"label": "Type of invocation",
|
||||||
"tooltipText": "Should the invocation be synchronous or asynchronous?",
|
"tooltipText": "Should the invocation be synchronous or asynchronous?",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
{
|
||||||
|
"identifier": "LIST_FUNCTION_ALIASES",
|
||||||
|
"controlType": "SECTION_V2",
|
||||||
|
"conditionals": {
|
||||||
|
"show": "{{actionConfiguration.formData.command.data === 'LIST_FUNCTION_ALIASES'}}"
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"controlType": "DOUBLE_COLUMN_ZONE",
|
||||||
|
"label": "Function details",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"label": "Function name",
|
||||||
|
"tooltipText": "The name of the AWS Lambda function to list aliases for.",
|
||||||
|
"subtitle": "",
|
||||||
|
"isRequired": true,
|
||||||
|
"propertyName": "function_name",
|
||||||
|
"configProperty": "actionConfiguration.formData.functionName.data",
|
||||||
|
"controlType": "DROP_DOWN",
|
||||||
|
"initialValue": "",
|
||||||
|
"options": [],
|
||||||
|
"placeholderText": "All function names will be fetched.",
|
||||||
|
"fetchOptionsConditionally": true,
|
||||||
|
"setFirstOptionAsDefault": true,
|
||||||
|
"alternateViewTypes": ["json"],
|
||||||
|
"conditionals": {
|
||||||
|
"enable": "{{true}}",
|
||||||
|
"fetchDynamicValues": {
|
||||||
|
"condition": "{{actionConfiguration.formData.command.data === 'LIST_FUNCTION_ALIASES'}}",
|
||||||
|
"config": {
|
||||||
|
"params": {
|
||||||
|
"requestType": "FUNCTION_NAMES",
|
||||||
|
"displayType": "DROP_DOWN"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
{
|
||||||
|
"identifier": "LIST_FUNCTION_VERSIONS",
|
||||||
|
"controlType": "SECTION_V2",
|
||||||
|
"conditionals": {
|
||||||
|
"show": "{{actionConfiguration.formData.command.data === 'LIST_FUNCTION_VERSIONS'}}"
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"controlType": "DOUBLE_COLUMN_ZONE",
|
||||||
|
"label": "Function details",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"label": "Function name",
|
||||||
|
"tooltipText": "The name of the AWS Lambda function to list versions for.",
|
||||||
|
"subtitle": "",
|
||||||
|
"isRequired": true,
|
||||||
|
"propertyName": "function_name",
|
||||||
|
"configProperty": "actionConfiguration.formData.functionName.data",
|
||||||
|
"controlType": "DROP_DOWN",
|
||||||
|
"initialValue": "",
|
||||||
|
"options": [],
|
||||||
|
"placeholderText": "All function names will be fetched.",
|
||||||
|
"fetchOptionsConditionally": true,
|
||||||
|
"setFirstOptionAsDefault": true,
|
||||||
|
"alternateViewTypes": ["json"],
|
||||||
|
"conditionals": {
|
||||||
|
"enable": "{{true}}",
|
||||||
|
"fetchDynamicValues": {
|
||||||
|
"condition": "{{actionConfiguration.formData.command.data === 'LIST_FUNCTION_VERSIONS'}}",
|
||||||
|
"config": {
|
||||||
|
"params": {
|
||||||
|
"requestType": "FUNCTION_NAMES",
|
||||||
|
"displayType": "DROP_DOWN"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -13,12 +13,21 @@
|
||||||
"description": "Choose the method you would like to use",
|
"description": "Choose the method you would like to use",
|
||||||
"configProperty": "actionConfiguration.formData.command.data",
|
"configProperty": "actionConfiguration.formData.command.data",
|
||||||
"controlType": "DROP_DOWN",
|
"controlType": "DROP_DOWN",
|
||||||
|
"isRequired": true,
|
||||||
"initialValue": "LIST_FUNCTIONS",
|
"initialValue": "LIST_FUNCTIONS",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"label": "List all functions",
|
"label": "List all functions",
|
||||||
"value": "LIST_FUNCTIONS"
|
"value": "LIST_FUNCTIONS"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "List function versions",
|
||||||
|
"value": "LIST_FUNCTION_VERSIONS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "List function aliases",
|
||||||
|
"value": "LIST_FUNCTION_ALIASES"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "Invoke a function",
|
"label": "Invoke a function",
|
||||||
"value": "INVOKE_FUNCTION"
|
"value": "INVOKE_FUNCTION"
|
||||||
|
|
@ -30,5 +39,5 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"files": ["list.json", "invoke.json"]
|
"files": ["list.json", "listVersions.json", "listAliases.json", "invoke.json"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,15 @@
|
||||||
package com.external.plugins;
|
package com.external.plugins;
|
||||||
|
|
||||||
import com.amazonaws.services.lambda.AWSLambda;
|
import com.amazonaws.services.lambda.AWSLambda;
|
||||||
|
import com.amazonaws.services.lambda.model.AliasConfiguration;
|
||||||
import com.amazonaws.services.lambda.model.FunctionConfiguration;
|
import com.amazonaws.services.lambda.model.FunctionConfiguration;
|
||||||
|
import com.amazonaws.services.lambda.model.InvokeRequest;
|
||||||
import com.amazonaws.services.lambda.model.InvokeResult;
|
import com.amazonaws.services.lambda.model.InvokeResult;
|
||||||
|
import com.amazonaws.services.lambda.model.ListAliasesRequest;
|
||||||
|
import com.amazonaws.services.lambda.model.ListAliasesResult;
|
||||||
import com.amazonaws.services.lambda.model.ListFunctionsResult;
|
import com.amazonaws.services.lambda.model.ListFunctionsResult;
|
||||||
|
import com.amazonaws.services.lambda.model.ListVersionsByFunctionRequest;
|
||||||
|
import com.amazonaws.services.lambda.model.ListVersionsByFunctionResult;
|
||||||
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException;
|
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException;
|
||||||
import com.appsmith.external.models.ActionConfiguration;
|
import com.appsmith.external.models.ActionConfiguration;
|
||||||
import com.appsmith.external.models.ActionExecutionResult;
|
import com.appsmith.external.models.ActionExecutionResult;
|
||||||
|
|
@ -14,6 +20,7 @@ import com.appsmith.external.models.TriggerRequestDTO;
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
import reactor.test.StepVerifier;
|
import reactor.test.StepVerifier;
|
||||||
|
|
@ -31,6 +38,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
@Testcontainers
|
@Testcontainers
|
||||||
|
|
@ -193,4 +201,305 @@ public class AwsLambdaPluginTest {
|
||||||
pluginExecutor.trigger(mockLambda, datasourceConfiguration, request).block();
|
pluginExecutor.trigger(mockLambda, datasourceConfiguration, request).block();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExecuteListFunctionVersions() {
|
||||||
|
DatasourceConfiguration datasourceConfiguration = createDatasourceConfiguration();
|
||||||
|
|
||||||
|
Map<String, Object> configMap = new HashMap<>();
|
||||||
|
setDataValueSafelyInFormData(configMap, "command", "LIST_FUNCTION_VERSIONS");
|
||||||
|
setDataValueSafelyInFormData(configMap, "functionName", "test-aws-lambda");
|
||||||
|
|
||||||
|
ActionConfiguration actionConfiguration = new ActionConfiguration();
|
||||||
|
actionConfiguration.setFormData(configMap);
|
||||||
|
|
||||||
|
// Mock the Lambda connection
|
||||||
|
AWSLambda mockLambda = mock(AWSLambda.class);
|
||||||
|
ListVersionsByFunctionResult mockVersionsResult = new ListVersionsByFunctionResult();
|
||||||
|
mockVersionsResult.setVersions(List.of(
|
||||||
|
new FunctionConfiguration().withVersion("$LATEST"),
|
||||||
|
new FunctionConfiguration().withVersion("1"),
|
||||||
|
new FunctionConfiguration().withVersion("2")));
|
||||||
|
when(mockLambda.listVersionsByFunction(any(ListVersionsByFunctionRequest.class)))
|
||||||
|
.thenReturn(mockVersionsResult);
|
||||||
|
|
||||||
|
Mono<ActionExecutionResult> resultMono =
|
||||||
|
pluginExecutor.execute(mockLambda, datasourceConfiguration, actionConfiguration);
|
||||||
|
StepVerifier.create(resultMono)
|
||||||
|
.assertNext(result -> {
|
||||||
|
assertEquals(3, ((ArrayNode) result.getBody()).size());
|
||||||
|
})
|
||||||
|
.verifyComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExecuteListFunctionAliases() {
|
||||||
|
DatasourceConfiguration datasourceConfiguration = createDatasourceConfiguration();
|
||||||
|
|
||||||
|
Map<String, Object> configMap = new HashMap<>();
|
||||||
|
setDataValueSafelyInFormData(configMap, "command", "LIST_FUNCTION_ALIASES");
|
||||||
|
setDataValueSafelyInFormData(configMap, "functionName", "test-aws-lambda");
|
||||||
|
|
||||||
|
ActionConfiguration actionConfiguration = new ActionConfiguration();
|
||||||
|
actionConfiguration.setFormData(configMap);
|
||||||
|
|
||||||
|
// Mock the Lambda connection
|
||||||
|
AWSLambda mockLambda = mock(AWSLambda.class);
|
||||||
|
ListAliasesResult mockAliasesResult = new ListAliasesResult();
|
||||||
|
mockAliasesResult.setAliases(
|
||||||
|
List.of(new AliasConfiguration().withName("PROD"), new AliasConfiguration().withName("STAGING")));
|
||||||
|
when(mockLambda.listAliases(any(ListAliasesRequest.class))).thenReturn(mockAliasesResult);
|
||||||
|
|
||||||
|
Mono<ActionExecutionResult> resultMono =
|
||||||
|
pluginExecutor.execute(mockLambda, datasourceConfiguration, actionConfiguration);
|
||||||
|
StepVerifier.create(resultMono)
|
||||||
|
.assertNext(result -> {
|
||||||
|
assertEquals(2, ((ArrayNode) result.getBody()).size());
|
||||||
|
})
|
||||||
|
.verifyComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExecuteInvokeFunctionWithVersion() {
|
||||||
|
DatasourceConfiguration datasourceConfiguration = createDatasourceConfiguration();
|
||||||
|
|
||||||
|
Map<String, Object> configMap = new HashMap<>();
|
||||||
|
setDataValueSafelyInFormData(configMap, "command", "INVOKE_FUNCTION");
|
||||||
|
setDataValueSafelyInFormData(configMap, "body", "{\"data\": \"\"}");
|
||||||
|
setDataValueSafelyInFormData(configMap, "functionName", "test-aws-lambda");
|
||||||
|
setDataValueSafelyInFormData(configMap, "functionVersion", "2");
|
||||||
|
|
||||||
|
ActionConfiguration actionConfiguration = new ActionConfiguration();
|
||||||
|
actionConfiguration.setFormData(configMap);
|
||||||
|
|
||||||
|
// Mock the Lambda connection
|
||||||
|
AWSLambda mockLambda = mock(AWSLambda.class);
|
||||||
|
InvokeResult mockResult = new InvokeResult();
|
||||||
|
mockResult.setPayload(ByteBuffer.wrap("Hello World from version 2".getBytes()));
|
||||||
|
when(mockLambda.invoke(any())).thenReturn(mockResult);
|
||||||
|
|
||||||
|
// Capture the InvokeRequest to verify the qualifier is set correctly
|
||||||
|
ArgumentCaptor<InvokeRequest> requestCaptor = ArgumentCaptor.forClass(InvokeRequest.class);
|
||||||
|
|
||||||
|
Mono<ActionExecutionResult> resultMono =
|
||||||
|
pluginExecutor.execute(mockLambda, datasourceConfiguration, actionConfiguration);
|
||||||
|
StepVerifier.create(resultMono)
|
||||||
|
.assertNext(result -> {
|
||||||
|
assertTrue(result.getIsExecutionSuccess());
|
||||||
|
assertEquals("Hello World from version 2", result.getBody().toString());
|
||||||
|
})
|
||||||
|
.verifyComplete();
|
||||||
|
|
||||||
|
// Verify that the InvokeRequest was called with the correct qualifier
|
||||||
|
verify(mockLambda).invoke(requestCaptor.capture());
|
||||||
|
InvokeRequest capturedRequest = requestCaptor.getValue();
|
||||||
|
assertEquals("test-aws-lambda", capturedRequest.getFunctionName());
|
||||||
|
assertEquals("2", capturedRequest.getQualifier());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExecuteInvokeFunctionWithAlias() {
|
||||||
|
DatasourceConfiguration datasourceConfiguration = createDatasourceConfiguration();
|
||||||
|
|
||||||
|
Map<String, Object> configMap = new HashMap<>();
|
||||||
|
setDataValueSafelyInFormData(configMap, "command", "INVOKE_FUNCTION");
|
||||||
|
setDataValueSafelyInFormData(configMap, "body", "{\"data\": \"\"}");
|
||||||
|
setDataValueSafelyInFormData(configMap, "functionName", "test-aws-lambda");
|
||||||
|
setDataValueSafelyInFormData(configMap, "functionAlias", "PROD");
|
||||||
|
|
||||||
|
ActionConfiguration actionConfiguration = new ActionConfiguration();
|
||||||
|
actionConfiguration.setFormData(configMap);
|
||||||
|
|
||||||
|
// Mock the Lambda connection
|
||||||
|
AWSLambda mockLambda = mock(AWSLambda.class);
|
||||||
|
InvokeResult mockResult = new InvokeResult();
|
||||||
|
mockResult.setPayload(ByteBuffer.wrap("Hello World from PROD alias".getBytes()));
|
||||||
|
when(mockLambda.invoke(any())).thenReturn(mockResult);
|
||||||
|
|
||||||
|
// Capture the InvokeRequest to verify the qualifier is set correctly
|
||||||
|
ArgumentCaptor<InvokeRequest> requestCaptor = ArgumentCaptor.forClass(InvokeRequest.class);
|
||||||
|
|
||||||
|
Mono<ActionExecutionResult> resultMono =
|
||||||
|
pluginExecutor.execute(mockLambda, datasourceConfiguration, actionConfiguration);
|
||||||
|
StepVerifier.create(resultMono)
|
||||||
|
.assertNext(result -> {
|
||||||
|
assertTrue(result.getIsExecutionSuccess());
|
||||||
|
assertEquals("Hello World from PROD alias", result.getBody().toString());
|
||||||
|
})
|
||||||
|
.verifyComplete();
|
||||||
|
|
||||||
|
// Verify that the InvokeRequest was called with the correct qualifier
|
||||||
|
verify(mockLambda).invoke(requestCaptor.capture());
|
||||||
|
InvokeRequest capturedRequest = requestCaptor.getValue();
|
||||||
|
assertEquals("test-aws-lambda", capturedRequest.getFunctionName());
|
||||||
|
assertEquals("PROD", capturedRequest.getQualifier());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExecuteInvokeFunctionWithAliasTakesPrecedenceOverVersion() {
|
||||||
|
DatasourceConfiguration datasourceConfiguration = createDatasourceConfiguration();
|
||||||
|
|
||||||
|
Map<String, Object> configMap = new HashMap<>();
|
||||||
|
setDataValueSafelyInFormData(configMap, "command", "INVOKE_FUNCTION");
|
||||||
|
setDataValueSafelyInFormData(configMap, "body", "{\"data\": \"\"}");
|
||||||
|
setDataValueSafelyInFormData(configMap, "functionName", "test-aws-lambda");
|
||||||
|
setDataValueSafelyInFormData(configMap, "functionVersion", "2");
|
||||||
|
setDataValueSafelyInFormData(configMap, "functionAlias", "PROD");
|
||||||
|
|
||||||
|
ActionConfiguration actionConfiguration = new ActionConfiguration();
|
||||||
|
actionConfiguration.setFormData(configMap);
|
||||||
|
|
||||||
|
// Mock the Lambda connection
|
||||||
|
AWSLambda mockLambda = mock(AWSLambda.class);
|
||||||
|
InvokeResult mockResult = new InvokeResult();
|
||||||
|
mockResult.setPayload(ByteBuffer.wrap("Hello World from PROD alias (alias takes precedence)".getBytes()));
|
||||||
|
when(mockLambda.invoke(any())).thenReturn(mockResult);
|
||||||
|
|
||||||
|
// Capture the InvokeRequest to verify the qualifier is set correctly
|
||||||
|
ArgumentCaptor<InvokeRequest> requestCaptor = ArgumentCaptor.forClass(InvokeRequest.class);
|
||||||
|
|
||||||
|
Mono<ActionExecutionResult> resultMono =
|
||||||
|
pluginExecutor.execute(mockLambda, datasourceConfiguration, actionConfiguration);
|
||||||
|
StepVerifier.create(resultMono)
|
||||||
|
.assertNext(result -> {
|
||||||
|
assertTrue(result.getIsExecutionSuccess());
|
||||||
|
assertEquals(
|
||||||
|
"Hello World from PROD alias (alias takes precedence)",
|
||||||
|
result.getBody().toString());
|
||||||
|
})
|
||||||
|
.verifyComplete();
|
||||||
|
|
||||||
|
// Verify that the InvokeRequest was called with the alias (not version) as qualifier
|
||||||
|
verify(mockLambda).invoke(requestCaptor.capture());
|
||||||
|
InvokeRequest capturedRequest = requestCaptor.getValue();
|
||||||
|
assertEquals("test-aws-lambda", capturedRequest.getFunctionName());
|
||||||
|
assertEquals("PROD", capturedRequest.getQualifier()); // Should be alias, not version "2"
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExecuteInvokeFunctionWithoutVersionOrAlias() {
|
||||||
|
DatasourceConfiguration datasourceConfiguration = createDatasourceConfiguration();
|
||||||
|
|
||||||
|
Map<String, Object> configMap = new HashMap<>();
|
||||||
|
setDataValueSafelyInFormData(configMap, "command", "INVOKE_FUNCTION");
|
||||||
|
setDataValueSafelyInFormData(configMap, "body", "{\"data\": \"\"}");
|
||||||
|
setDataValueSafelyInFormData(configMap, "functionName", "test-aws-lambda");
|
||||||
|
// No functionVersion or functionAlias specified
|
||||||
|
|
||||||
|
ActionConfiguration actionConfiguration = new ActionConfiguration();
|
||||||
|
actionConfiguration.setFormData(configMap);
|
||||||
|
|
||||||
|
// Mock the Lambda connection
|
||||||
|
AWSLambda mockLambda = mock(AWSLambda.class);
|
||||||
|
InvokeResult mockResult = new InvokeResult();
|
||||||
|
mockResult.setPayload(ByteBuffer.wrap("Hello World from $LATEST".getBytes()));
|
||||||
|
when(mockLambda.invoke(any())).thenReturn(mockResult);
|
||||||
|
|
||||||
|
// Capture the InvokeRequest to verify no qualifier is set (defaults to $LATEST)
|
||||||
|
ArgumentCaptor<InvokeRequest> requestCaptor = ArgumentCaptor.forClass(InvokeRequest.class);
|
||||||
|
|
||||||
|
Mono<ActionExecutionResult> resultMono =
|
||||||
|
pluginExecutor.execute(mockLambda, datasourceConfiguration, actionConfiguration);
|
||||||
|
StepVerifier.create(resultMono)
|
||||||
|
.assertNext(result -> {
|
||||||
|
assertTrue(result.getIsExecutionSuccess());
|
||||||
|
assertEquals("Hello World from $LATEST", result.getBody().toString());
|
||||||
|
})
|
||||||
|
.verifyComplete();
|
||||||
|
|
||||||
|
// Verify that the InvokeRequest was called without a qualifier (defaults to $LATEST)
|
||||||
|
verify(mockLambda).invoke(requestCaptor.capture());
|
||||||
|
InvokeRequest capturedRequest = requestCaptor.getValue();
|
||||||
|
assertEquals("test-aws-lambda", capturedRequest.getFunctionName());
|
||||||
|
// When no qualifier is set, it should be null (AWS defaults to $LATEST)
|
||||||
|
assertEquals(null, capturedRequest.getQualifier());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTriggerFunctionNames() {
|
||||||
|
AWSLambda mockLambda = mock(AWSLambda.class);
|
||||||
|
DatasourceConfiguration datasourceConfiguration = createDatasourceConfiguration();
|
||||||
|
|
||||||
|
ListFunctionsResult mockFunctionsResult = new ListFunctionsResult();
|
||||||
|
mockFunctionsResult.setFunctions(List.of(
|
||||||
|
new FunctionConfiguration().withFunctionName("function1"),
|
||||||
|
new FunctionConfiguration().withFunctionName("function2")));
|
||||||
|
when(mockLambda.listFunctions()).thenReturn(mockFunctionsResult);
|
||||||
|
|
||||||
|
TriggerRequestDTO request = new TriggerRequestDTO();
|
||||||
|
request.setRequestType("FUNCTION_NAMES");
|
||||||
|
|
||||||
|
Mono<com.appsmith.external.models.TriggerResultDTO> resultMono =
|
||||||
|
pluginExecutor.trigger(mockLambda, datasourceConfiguration, request);
|
||||||
|
StepVerifier.create(resultMono)
|
||||||
|
.assertNext(result -> {
|
||||||
|
assertEquals(2, ((List<?>) result.getTrigger()).size());
|
||||||
|
})
|
||||||
|
.verifyComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTriggerFunctionVersions() {
|
||||||
|
AWSLambda mockLambda = mock(AWSLambda.class);
|
||||||
|
DatasourceConfiguration datasourceConfiguration = createDatasourceConfiguration();
|
||||||
|
|
||||||
|
ListVersionsByFunctionResult mockVersionsResult = new ListVersionsByFunctionResult();
|
||||||
|
mockVersionsResult.setVersions(List.of(
|
||||||
|
new FunctionConfiguration().withVersion("$LATEST"),
|
||||||
|
new FunctionConfiguration().withVersion("1"),
|
||||||
|
new FunctionConfiguration().withVersion("2")));
|
||||||
|
when(mockLambda.listVersionsByFunction(any(ListVersionsByFunctionRequest.class)))
|
||||||
|
.thenReturn(mockVersionsResult);
|
||||||
|
|
||||||
|
TriggerRequestDTO request = new TriggerRequestDTO();
|
||||||
|
request.setRequestType("FUNCTION_VERSIONS");
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("functionName", "test-function");
|
||||||
|
request.setParameters(params);
|
||||||
|
|
||||||
|
Mono<com.appsmith.external.models.TriggerResultDTO> resultMono =
|
||||||
|
pluginExecutor.trigger(mockLambda, datasourceConfiguration, request);
|
||||||
|
StepVerifier.create(resultMono)
|
||||||
|
.assertNext(result -> {
|
||||||
|
assertEquals(3, ((List<?>) result.getTrigger()).size());
|
||||||
|
})
|
||||||
|
.verifyComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTriggerFunctionAliases() {
|
||||||
|
AWSLambda mockLambda = mock(AWSLambda.class);
|
||||||
|
DatasourceConfiguration datasourceConfiguration = createDatasourceConfiguration();
|
||||||
|
|
||||||
|
ListAliasesResult mockAliasesResult = new ListAliasesResult();
|
||||||
|
mockAliasesResult.setAliases(
|
||||||
|
List.of(new AliasConfiguration().withName("PROD"), new AliasConfiguration().withName("STAGING")));
|
||||||
|
when(mockLambda.listAliases(any(ListAliasesRequest.class))).thenReturn(mockAliasesResult);
|
||||||
|
|
||||||
|
TriggerRequestDTO request = new TriggerRequestDTO();
|
||||||
|
request.setRequestType("FUNCTION_ALIASES");
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("functionName", "test-function");
|
||||||
|
request.setParameters(params);
|
||||||
|
|
||||||
|
Mono<com.appsmith.external.models.TriggerResultDTO> resultMono =
|
||||||
|
pluginExecutor.trigger(mockLambda, datasourceConfiguration, request);
|
||||||
|
StepVerifier.create(resultMono)
|
||||||
|
.assertNext(result -> {
|
||||||
|
assertEquals(2, ((List<?>) result.getTrigger()).size());
|
||||||
|
})
|
||||||
|
.verifyComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTriggerUnsupportedRequestType() {
|
||||||
|
AWSLambda mockLambda = mock(AWSLambda.class);
|
||||||
|
DatasourceConfiguration datasourceConfiguration = createDatasourceConfiguration();
|
||||||
|
TriggerRequestDTO request = new TriggerRequestDTO();
|
||||||
|
request.setRequestType("UNSUPPORTED_TYPE");
|
||||||
|
|
||||||
|
assertThrows(AppsmithPluginException.class, () -> {
|
||||||
|
pluginExecutor.trigger(mockLambda, datasourceConfiguration, request).block();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user