Skip to content

Commit a8752c7

Browse files
committed
feat: add support for listing function versions and aliases in AWS Lambda Plugin
- Implemented new commands `LIST_FUNCTION_VERSIONS` and `LIST_FUNCTION_ALIASES` in the AwsLambdaPlugin to retrieve function versions and aliases respectively. - Updated the invoke.json to include optional fields for function version and alias. - Created new JSON files for listing function versions and aliases with appropriate configurations. - Enhanced the plugin's test suite to cover the new functionalities, ensuring correct behavior for version and alias retrieval. This update improves the AWS Lambda Plugin's capabilities, allowing users to manage function versions and aliases more effectively.
1 parent b79b160 commit a8752c7

6 files changed

Lines changed: 518 additions & 11 deletions

File tree

app/server/appsmith-plugins/awsLambdaPlugin/src/main/java/com/external/plugins/AwsLambdaPlugin.java

Lines changed: 128 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@
99
import com.amazonaws.services.lambda.model.FunctionConfiguration;
1010
import com.amazonaws.services.lambda.model.InvokeRequest;
1111
import com.amazonaws.services.lambda.model.InvokeResult;
12+
import com.amazonaws.services.lambda.model.ListAliasesRequest;
13+
import com.amazonaws.services.lambda.model.ListAliasesResult;
1214
import com.amazonaws.services.lambda.model.ListFunctionsResult;
15+
import com.amazonaws.services.lambda.model.ListVersionsByFunctionRequest;
16+
import com.amazonaws.services.lambda.model.ListVersionsByFunctionResult;
1317
import com.amazonaws.services.lambda.model.ResourceNotFoundException;
1418
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError;
1519
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException;
@@ -70,6 +74,10 @@ public Mono<ActionExecutionResult> execute(
7074
ActionExecutionResult result;
7175
switch (Objects.requireNonNull(command)) {
7276
case "LIST_FUNCTIONS" -> result = listFunctions(actionConfiguration, connection);
77+
case "LIST_FUNCTION_VERSIONS" -> result =
78+
listFunctionVersions(actionConfiguration, connection);
79+
case "LIST_FUNCTION_ALIASES" -> result =
80+
listFunctionAliases(actionConfiguration, connection);
7381
case "INVOKE_FUNCTION" -> result = invokeFunction(actionConfiguration, connection);
7482
default -> throw new IllegalStateException("Unexpected value: " + command);
7583
}
@@ -98,24 +106,84 @@ public Mono<TriggerResultDTO> trigger(
98106
throw new AppsmithPluginException(
99107
AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR, "request type is missing");
100108
}
101-
ActionExecutionResult actionExecutionResult = listFunctions(null, connection);
102-
ArrayNode body = (ArrayNode) actionExecutionResult.getBody();
103-
List<Map<String, String>> functionNames = StreamSupport.stream(body.spliterator(), false)
104-
.map(function -> function.get("functionName").asText())
105-
.sorted()
106-
.map(functionName -> Map.of("label", functionName, "value", functionName))
107-
.collect(Collectors.toList());
109+
110+
String requestType = request.getRequestType();
111+
ActionExecutionResult actionExecutionResult;
112+
List<Map<String, String>> options;
113+
114+
switch (requestType) {
115+
case "FUNCTION_NAMES" -> {
116+
actionExecutionResult = listFunctions(null, connection);
117+
ArrayNode body = (ArrayNode) actionExecutionResult.getBody();
118+
options = StreamSupport.stream(body.spliterator(), false)
119+
.map(function -> function.get("functionName").asText())
120+
.sorted()
121+
.map(functionName -> Map.of("label", functionName, "value", functionName))
122+
.collect(Collectors.toList());
123+
}
124+
case "FUNCTION_VERSIONS" -> {
125+
String functionName = request.getParams().get("functionName");
126+
if (!StringUtils.hasText(functionName)) {
127+
throw new AppsmithPluginException(
128+
AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR,
129+
"function name is required for listing versions");
130+
}
131+
actionExecutionResult = listFunctionVersions(null, connection, functionName);
132+
ArrayNode body = (ArrayNode) actionExecutionResult.getBody();
133+
options = StreamSupport.stream(body.spliterator(), false)
134+
.map(version -> version.get("version").asText())
135+
.sorted()
136+
.map(version -> Map.of("label", version, "value", version))
137+
.collect(Collectors.toList());
138+
}
139+
case "FUNCTION_ALIASES" -> {
140+
String functionName = request.getParams().get("functionName");
141+
if (!StringUtils.hasText(functionName)) {
142+
throw new AppsmithPluginException(
143+
AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR,
144+
"function name is required for listing aliases");
145+
}
146+
actionExecutionResult = listFunctionAliases(null, connection, functionName);
147+
ArrayNode body = (ArrayNode) actionExecutionResult.getBody();
148+
options = StreamSupport.stream(body.spliterator(), false)
149+
.map(alias -> alias.get("name").asText())
150+
.sorted()
151+
.map(alias -> Map.of("label", alias, "value", alias))
152+
.collect(Collectors.toList());
153+
}
154+
default -> throw new AppsmithPluginException(
155+
AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR, "Unsupported request type: " + requestType);
156+
}
108157

109158
TriggerResultDTO triggerResultDTO = new TriggerResultDTO();
110-
triggerResultDTO.setTrigger(functionNames);
159+
triggerResultDTO.setTrigger(options);
111160

112161
return Mono.just(triggerResultDTO);
113162
}
114163

115164
ActionExecutionResult invokeFunction(ActionConfiguration actionConfiguration, AWSLambda connection) {
116165
InvokeRequest invokeRequest = new InvokeRequest();
117-
invokeRequest.setFunctionName(
118-
getDataValueSafelyFromFormData(actionConfiguration.getFormData(), "functionName", STRING_TYPE));
166+
167+
// Build function name with version/alias if specified
168+
String functionName =
169+
getDataValueSafelyFromFormData(actionConfiguration.getFormData(), "functionName", STRING_TYPE);
170+
String functionVersion =
171+
getDataValueSafelyFromFormData(actionConfiguration.getFormData(), "functionVersion", STRING_TYPE);
172+
String functionAlias =
173+
getDataValueSafelyFromFormData(actionConfiguration.getFormData(), "functionAlias", STRING_TYPE);
174+
175+
// Construct the full function name
176+
String fullFunctionName = functionName;
177+
if (StringUtils.hasText(functionAlias)) {
178+
// If alias is specified, use it (alias takes precedence over version)
179+
fullFunctionName = functionName + ":" + functionAlias;
180+
} else if (StringUtils.hasText(functionVersion)) {
181+
// If version is specified and no alias, use version
182+
fullFunctionName = functionName + ":" + functionVersion;
183+
}
184+
// If neither version nor alias is specified, use the function name as-is (defaults to $LATEST)
185+
186+
invokeRequest.setFunctionName(fullFunctionName);
119187
invokeRequest.setPayload(
120188
getDataValueSafelyFromFormData(actionConfiguration.getFormData(), "body", STRING_TYPE));
121189
invokeRequest.setInvocationType(
@@ -145,6 +213,56 @@ ActionExecutionResult listFunctions(ActionConfiguration actionConfiguration, AWS
145213
return result;
146214
}
147215

216+
ActionExecutionResult listFunctionVersions(
217+
ActionConfiguration actionConfiguration, AWSLambda connection, String functionName) {
218+
if (actionConfiguration != null) {
219+
functionName =
220+
getDataValueSafelyFromFormData(actionConfiguration.getFormData(), "functionName", STRING_TYPE);
221+
}
222+
223+
ListVersionsByFunctionRequest request = new ListVersionsByFunctionRequest();
224+
request.setFunctionName(functionName);
225+
226+
ListVersionsByFunctionResult listVersionsResult = connection.listVersionsByFunction(request);
227+
List<FunctionConfiguration> versions = listVersionsResult.getVersions();
228+
229+
ActionExecutionResult result = new ActionExecutionResult();
230+
result.setBody(objectMapper.valueToTree(versions));
231+
result.setIsExecutionSuccess(true);
232+
return result;
233+
}
234+
235+
ActionExecutionResult listFunctionVersions(ActionConfiguration actionConfiguration, AWSLambda connection) {
236+
String functionName =
237+
getDataValueSafelyFromFormData(actionConfiguration.getFormData(), "functionName", STRING_TYPE);
238+
return listFunctionVersions(null, connection, functionName);
239+
}
240+
241+
ActionExecutionResult listFunctionAliases(
242+
ActionConfiguration actionConfiguration, AWSLambda connection, String functionName) {
243+
if (actionConfiguration != null) {
244+
functionName =
245+
getDataValueSafelyFromFormData(actionConfiguration.getFormData(), "functionName", STRING_TYPE);
246+
}
247+
248+
ListAliasesRequest request = new ListAliasesRequest();
249+
request.setFunctionName(functionName);
250+
251+
ListAliasesResult listAliasesResult = connection.listAliases(request);
252+
List<com.amazonaws.services.lambda.model.AliasConfiguration> aliases = listAliasesResult.getAliases();
253+
254+
ActionExecutionResult result = new ActionExecutionResult();
255+
result.setBody(objectMapper.valueToTree(aliases));
256+
result.setIsExecutionSuccess(true);
257+
return result;
258+
}
259+
260+
ActionExecutionResult listFunctionAliases(ActionConfiguration actionConfiguration, AWSLambda connection) {
261+
String functionName =
262+
getDataValueSafelyFromFormData(actionConfiguration.getFormData(), "functionName", STRING_TYPE);
263+
return listFunctionAliases(null, connection, functionName);
264+
}
265+
148266
@Override
149267
public Mono<AWSLambda> datasourceCreate(DatasourceConfiguration datasourceConfiguration) {
150268
log.debug(Thread.currentThread().getName() + ": datasourceCreate() called for AWS Lambda plugin.");

app/server/appsmith-plugins/awsLambdaPlugin/src/main/resources/editor/invoke.json

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,62 @@
3636
}
3737
}
3838
},
39+
{
40+
"label": "Function version",
41+
"tooltipText": "Optional: Specify a version number (e.g., 1, 2, $LATEST) or leave empty for $LATEST.",
42+
"subtitle": "",
43+
"isRequired": false,
44+
"propertyName": "function_version",
45+
"configProperty": "actionConfiguration.formData.functionVersion.data",
46+
"controlType": "DROP_DOWN",
47+
"initialValue": "",
48+
"options": [],
49+
"placeholderText": "Leave empty for $LATEST version",
50+
"fetchOptionsConditionally": true,
51+
"setFirstOptionAsDefault": false,
52+
"alternateViewTypes": ["json"],
53+
"conditionals": {
54+
"enable": "{{actionConfiguration.formData.functionName.data}}",
55+
"fetchDynamicValues": {
56+
"condition": "{{actionConfiguration.formData.command.data === 'INVOKE_FUNCTION' && actionConfiguration.formData.functionName.data}}",
57+
"config": {
58+
"params": {
59+
"requestType": "FUNCTION_VERSIONS",
60+
"displayType": "DROP_DOWN",
61+
"functionName": "{{actionConfiguration.formData.functionName.data}}"
62+
}
63+
}
64+
}
65+
}
66+
},
67+
{
68+
"label": "Function alias",
69+
"tooltipText": "Optional: Specify an alias name (e.g., PROD, STAGING) or leave empty for no alias.",
70+
"subtitle": "",
71+
"isRequired": false,
72+
"propertyName": "function_alias",
73+
"configProperty": "actionConfiguration.formData.functionAlias.data",
74+
"controlType": "DROP_DOWN",
75+
"initialValue": "",
76+
"options": [],
77+
"placeholderText": "Leave empty for no alias",
78+
"fetchOptionsConditionally": true,
79+
"setFirstOptionAsDefault": false,
80+
"alternateViewTypes": ["json"],
81+
"conditionals": {
82+
"enable": "{{actionConfiguration.formData.functionName.data}}",
83+
"fetchDynamicValues": {
84+
"condition": "{{actionConfiguration.formData.command.data === 'INVOKE_FUNCTION' && actionConfiguration.formData.functionName.data}}",
85+
"config": {
86+
"params": {
87+
"requestType": "FUNCTION_ALIASES",
88+
"displayType": "DROP_DOWN",
89+
"functionName": "{{actionConfiguration.formData.functionName.data}}"
90+
}
91+
}
92+
}
93+
}
94+
},
3995
{
4096
"label": "Type of invocation",
4197
"tooltipText": "Should the invocation be synchronous or asynchronous?",
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"identifier": "LIST_FUNCTION_ALIASES",
3+
"controlType": "SECTION_V2",
4+
"conditionals": {
5+
"show": "{{actionConfiguration.formData.command.data === 'LIST_FUNCTION_ALIASES'}}"
6+
},
7+
"children": [
8+
{
9+
"controlType": "DOUBLE_COLUMN_ZONE",
10+
"label": "Function details",
11+
"children": [
12+
{
13+
"label": "Function name",
14+
"tooltipText": "The name of the AWS Lambda function to list aliases for.",
15+
"subtitle": "",
16+
"isRequired": true,
17+
"propertyName": "function_name",
18+
"configProperty": "actionConfiguration.formData.functionName.data",
19+
"controlType": "DROP_DOWN",
20+
"initialValue": "",
21+
"options": [],
22+
"placeholderText": "All function names will be fetched.",
23+
"fetchOptionsConditionally": true,
24+
"setFirstOptionAsDefault": true,
25+
"alternateViewTypes": ["json"],
26+
"conditionals": {
27+
"enable": "{{true}}",
28+
"fetchDynamicValues": {
29+
"condition": "{{actionConfiguration.formData.command.data === 'LIST_FUNCTION_ALIASES'}}",
30+
"config": {
31+
"params": {
32+
"requestType": "FUNCTION_NAMES",
33+
"displayType": "DROP_DOWN"
34+
}
35+
}
36+
}
37+
}
38+
}
39+
]
40+
}
41+
]
42+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"identifier": "LIST_FUNCTION_VERSIONS",
3+
"controlType": "SECTION_V2",
4+
"conditionals": {
5+
"show": "{{actionConfiguration.formData.command.data === 'LIST_FUNCTION_VERSIONS'}}"
6+
},
7+
"children": [
8+
{
9+
"controlType": "DOUBLE_COLUMN_ZONE",
10+
"label": "Function details",
11+
"children": [
12+
{
13+
"label": "Function name",
14+
"tooltipText": "The name of the AWS Lambda function to list versions for.",
15+
"subtitle": "",
16+
"isRequired": true,
17+
"propertyName": "function_name",
18+
"configProperty": "actionConfiguration.formData.functionName.data",
19+
"controlType": "DROP_DOWN",
20+
"initialValue": "",
21+
"options": [],
22+
"placeholderText": "All function names will be fetched.",
23+
"fetchOptionsConditionally": true,
24+
"setFirstOptionAsDefault": true,
25+
"alternateViewTypes": ["json"],
26+
"conditionals": {
27+
"enable": "{{true}}",
28+
"fetchDynamicValues": {
29+
"condition": "{{actionConfiguration.formData.command.data === 'LIST_FUNCTION_VERSIONS'}}",
30+
"config": {
31+
"params": {
32+
"requestType": "FUNCTION_NAMES",
33+
"displayType": "DROP_DOWN"
34+
}
35+
}
36+
}
37+
}
38+
}
39+
]
40+
}
41+
]
42+
}

app/server/appsmith-plugins/awsLambdaPlugin/src/main/resources/editor/root.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,21 @@
1313
"description": "Choose the method you would like to use",
1414
"configProperty": "actionConfiguration.formData.command.data",
1515
"controlType": "DROP_DOWN",
16+
"isRequired": true,
1617
"initialValue": "LIST_FUNCTIONS",
1718
"options": [
1819
{
1920
"label": "List all functions",
2021
"value": "LIST_FUNCTIONS"
2122
},
23+
{
24+
"label": "List function versions",
25+
"value": "LIST_FUNCTION_VERSIONS"
26+
},
27+
{
28+
"label": "List function aliases",
29+
"value": "LIST_FUNCTION_ALIASES"
30+
},
2231
{
2332
"label": "Invoke a function",
2433
"value": "INVOKE_FUNCTION"
@@ -30,5 +39,5 @@
3039
]
3140
}
3241
],
33-
"files": ["list.json", "invoke.json"]
42+
"files": ["list.json", "listVersions.json", "listAliases.json", "invoke.json"]
3443
}

0 commit comments

Comments
 (0)