Skip to content

Commit 87ffca2

Browse files
authored
List anonymous HTTP triggers after function deployment (#4508)
* List annoymous http triggers after function deployment * Provide function access keys document after non-anonymous triggers deployed * Adopt stable tools common library
1 parent ac59e68 commit 87ffca2

File tree

2 files changed

+87
-3
lines changed

2 files changed

+87
-3
lines changed

PluginsAndFeatures/azure-toolkit-for-intellij/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ dependencies {
111111
exclude group: "javax.xml.bind", module: "jaxb-api"
112112
}
113113
compile group: 'com.microsoft.azure', name: 'azure-auth-helper', version: '0.4.0'
114-
compile 'com.microsoft.azure:azure-tools-common:0.4.0'
114+
compile 'com.microsoft.azure:azure-tools-common:0.5.0'
115115
compile group: 'com.microsoft.azure.appplatform.v2019_05_01_preview', name: 'azure-mgmt-appplatform', version: '1.0.0-beta-2'
116116
compile group: 'org.dom4j', name: 'dom4j', version: '2.1.3'
117117
compile group: 'jaxen', name: 'jaxen', version: '1.2.0'

PluginsAndFeatures/azure-toolkit-for-intellij/src/com/microsoft/intellij/runner/functions/library/function/DeployFunctionHandler.java

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,27 +34,35 @@
3434
import com.microsoft.azure.common.function.handlers.artifact.MSDeployArtifactHandlerImpl;
3535
import com.microsoft.azure.common.function.handlers.artifact.RunFromBlobArtifactHandlerImpl;
3636
import com.microsoft.azure.common.function.handlers.artifact.RunFromZipArtifactHandlerImpl;
37+
import com.microsoft.azure.common.function.model.FunctionResource;
3738
import com.microsoft.azure.common.handlers.ArtifactHandler;
3839
import com.microsoft.azure.common.handlers.artifact.ArtifactHandlerBase;
3940
import com.microsoft.azure.common.handlers.artifact.FTPArtifactHandlerImpl;
4041
import com.microsoft.azure.common.handlers.artifact.ZIPArtifactHandlerImpl;
4142
import com.microsoft.azure.common.utils.AppServiceUtils;
43+
import com.microsoft.azure.functions.annotation.AuthorizationLevel;
4244
import com.microsoft.azure.management.appservice.FunctionApp;
4345
import com.microsoft.azure.management.appservice.FunctionApp.Update;
4446
import com.microsoft.intellij.runner.functions.library.IAppServiceContext;
4547
import com.microsoft.intellij.runner.functions.library.IPrompter;
48+
import org.apache.commons.collections4.CollectionUtils;
4649
import org.apache.commons.lang3.StringUtils;
4750

51+
import java.io.IOException;
52+
import java.util.List;
4853
import java.util.Map;
4954
import java.util.function.Consumer;
55+
import java.util.stream.Collectors;
5056

5157
import static com.microsoft.azure.common.appservice.DeploymentType.*;
5258

5359
/**
54-
* Deploy artifacts to target Azure Functions in Azure. If target Azure
55-
* Functions doesn't exist, it will be created.
60+
* Deploy artifacts to target Azure Functions in Azure.
61+
* Todo: Move the handler to tools-common
5662
*/
5763
public class DeployFunctionHandler {
64+
private static final int LIST_TRIGGERS_MAX_RETRY = 3;
65+
private static final int LIST_TRIGGERS_RETRY_PERIOD_IN_SECONDS = 10;
5866
private static final String FUNCTIONS_WORKER_RUNTIME_NAME = "FUNCTIONS_WORKER_RUNTIME";
5967
private static final String FUNCTIONS_WORKER_RUNTIME_VALUE = "java";
6068
private static final String SET_FUNCTIONS_WORKER_RUNTIME = "Set function worker runtime to java";
@@ -70,6 +78,19 @@ public class DeployFunctionHandler {
7078
private static final String FUNCTION_APP_UPDATE_DONE = "Successfully updated the function app %s.";
7179
private static final String UNKNOW_DEPLOYMENT_TYPE = "The value of <deploymentType> is unknown, supported values are: " +
7280
"ftp, zip, msdeploy, run_from_blob and run_from_zip.";
81+
private static final String FAILED_TO_LIST_TRIGGERS = "Deployment succeeded, but failed to list http trigger urls.";
82+
private static final String UNABLE_TO_LIST_NONE_ANONYMOUS_HTTP_TRIGGERS = "Some http trigger urls cannot be displayed " +
83+
"because they are non-anonymous. To access the non-anonymous triggers, "
84+
+ "please refer https://aka.ms/azure-functions-key.";
85+
private static final String HTTP_TRIGGER_URLS = "HTTP Trigger Urls:";
86+
private static final String NO_ANONYMOUS_HTTP_TRIGGER = "No anonymous HTTP Triggers found in deployed function app, "
87+
+ "skip list triggers.";
88+
private static final String AUTH_LEVEL = "authLevel";
89+
private static final String HTTP_TRIGGER = "httpTrigger";
90+
private static final String NO_TRIGGERS_FOUNDED = "No triggers found in deployed function app, " +
91+
"please try recompile the project by `Build` -> `Build Project` and deploy again.";
92+
private static final String SYNCING_TRIGGERS_AND_FETCH_FUNCTION_INFORMATION = "Syncing triggers and fetching "
93+
+ "function information (Attempt %d/%d)...";
7394

7495
private static final OperatingSystemEnum DEFAULT_OS = OperatingSystemEnum.Windows;
7596
private IAppServiceContext ctx;
@@ -89,6 +110,7 @@ public FunctionApp execute() throws Exception {
89110
prompt(DEPLOY_START);
90111
getArtifactHandler().publish(deployTarget);
91112
prompt(String.format(DEPLOY_FINISH, ctx.getAppName()));
113+
listHTTPTriggerUrls();
92114
return (FunctionApp) deployTarget.getApp();
93115
}
94116

@@ -107,6 +129,68 @@ private void configureAppSettings(final Consumer<Map> withAppSettings, final Map
107129
}
108130
}
109131

132+
/**
133+
* List anonymous HTTP Triggers url after deployment
134+
*/
135+
private void listHTTPTriggerUrls() {
136+
try {
137+
final List<FunctionResource> triggers = listFunctions();
138+
final List<FunctionResource> httpFunction =
139+
triggers.stream()
140+
.filter(function -> function.getTrigger() != null &&
141+
StringUtils.equalsIgnoreCase(function.getTrigger().getType(), HTTP_TRIGGER))
142+
.collect(Collectors.toList());
143+
final List<FunctionResource> anonymousTriggers =
144+
httpFunction.stream()
145+
.filter(bindingResource -> bindingResource.getTrigger() != null &&
146+
StringUtils.equalsIgnoreCase(
147+
(CharSequence) bindingResource.getTrigger().getProperty(AUTH_LEVEL),
148+
AuthorizationLevel.ANONYMOUS.toString()))
149+
.collect(Collectors.toList());
150+
if (CollectionUtils.isEmpty(httpFunction) || CollectionUtils.isEmpty(anonymousTriggers)) {
151+
prompt(NO_ANONYMOUS_HTTP_TRIGGER);
152+
return;
153+
}
154+
prompt(HTTP_TRIGGER_URLS);
155+
anonymousTriggers.forEach(trigger -> prompt(String.format("\t %s : %s", trigger.getName(), trigger.getTriggerUrl())));
156+
if (anonymousTriggers.size() < httpFunction.size()) {
157+
prompt(UNABLE_TO_LIST_NONE_ANONYMOUS_HTTP_TRIGGERS);
158+
}
159+
} catch (InterruptedException | IOException e) {
160+
prompt(FAILED_TO_LIST_TRIGGERS);
161+
} catch (AzureExecutionException e) {
162+
prompt(e.getMessage());
163+
}
164+
}
165+
166+
/**
167+
* Sync triggers and return function list of deployed function app
168+
* Will retry when get empty result, the max retry times is LIST_TRIGGERS_MAX_RETRY
169+
* @return List of functions in deployed function app
170+
* @throws AzureExecutionException Throw if get empty result after LIST_TRIGGERS_MAX_RETRY times retry
171+
* @throws IOException Throw if meet IOException while getting Azure client
172+
* @throws InterruptedException Throw when thread was interrupted while sleeping between retry
173+
*/
174+
private List<FunctionResource> listFunctions() throws AzureExecutionException, InterruptedException, IOException {
175+
final FunctionApp functionApp = getFunctionApp();
176+
for (int i = 0; i < LIST_TRIGGERS_MAX_RETRY; i++) {
177+
Thread.sleep(LIST_TRIGGERS_RETRY_PERIOD_IN_SECONDS * 1000);
178+
prompt(String.format(SYNCING_TRIGGERS_AND_FETCH_FUNCTION_INFORMATION, i + 1, LIST_TRIGGERS_MAX_RETRY));
179+
functionApp.syncTriggers();
180+
final List<FunctionResource> triggers =
181+
ctx.getAzureClient().appServices().functionApps()
182+
.listFunctions(ctx.getResourceGroup(),
183+
ctx.getAppName()).stream()
184+
.map(envelope -> FunctionResource.parseFunction(envelope))
185+
.filter(function -> function != null)
186+
.collect(Collectors.toList());
187+
if (CollectionUtils.isNotEmpty(triggers)) {
188+
return triggers;
189+
}
190+
}
191+
throw new AzureExecutionException(NO_TRIGGERS_FOUNDED);
192+
}
193+
110194
private OperatingSystemEnum getOsEnum() throws AzureExecutionException {
111195
final RuntimeConfiguration runtime = ctx.getRuntime();
112196
if (runtime != null && StringUtils.isNotBlank(runtime.getOs())) {

0 commit comments

Comments
 (0)