From 323ee2f5ca8ee89c6e639660cb8f06c798421602 Mon Sep 17 00:00:00 2001 From: Sheng Chen Date: Mon, 11 Dec 2017 22:34:59 -0800 Subject: [PATCH] Change the validator of function name (#69) * change the validator of function name * add test case for azure-functions:add goal * remove redundant back-slashed --- azure-functions-maven-plugin/pom.xml | 2 +- .../azure/maven/function/AddMojo.java | 14 ++++++--- .../src/main/resources/templates.json | 8 ++--- .../azure/maven/function/AddMojoTest.java | 30 +++++++++++++++++++ 4 files changed, 45 insertions(+), 9 deletions(-) diff --git a/azure-functions-maven-plugin/pom.xml b/azure-functions-maven-plugin/pom.xml index 373a6e4cd5..497f4f684f 100644 --- a/azure-functions-maven-plugin/pom.xml +++ b/azure-functions-maven-plugin/pom.xml @@ -6,7 +6,7 @@ com.microsoft.azure azure-functions-maven-plugin - 0.1.8-SNAPSHOT + 0.1.9-SNAPSHOT maven-plugin Maven Plugin for Azure Functions Maven Plugin for Azure Functions diff --git a/azure-functions-maven-plugin/src/main/java/com/microsoft/azure/maven/function/AddMojo.java b/azure-functions-maven-plugin/src/main/java/com/microsoft/azure/maven/function/AddMojo.java index 2ab8d0c96a..d6e940e59b 100644 --- a/azure-functions-maven-plugin/src/main/java/com/microsoft/azure/maven/function/AddMojo.java +++ b/azure-functions-maven-plugin/src/main/java/com/microsoft/azure/maven/function/AddMojo.java @@ -47,6 +47,7 @@ public class AddMojo extends AbstractFunctionMojo { public static final String SAVE_FILE = "Step 4 of 4: Saving function to file"; public static final String SAVE_FILE_DONE = "Successfully saved new function at "; public static final String FILE_EXIST = "Function already exists at %s. Please specify a different function name."; + private static final String FUNCTION_NAME_REGEXP = "^[a-zA-Z][a-zA-Z\\d_\\-]*$"; //region Properties @@ -92,6 +93,10 @@ public String getFunctionName() { return functionName; } + public String getClassName() { + return getFunctionName().replace('-', '_'); + } + public String getFunctionTemplate() { return functionTemplate; } @@ -219,6 +224,7 @@ protected Map prepareRequiredParameters(final FunctionTemplate t final Map params = new HashMap<>(); params.put("functionName", getFunctionName()); + params.put("className", getClassName()); params.put("packageName", getFunctionPackageName()); prepareTemplateParameters(template, params); @@ -233,14 +239,14 @@ protected void prepareFunctionName() throws MojoFailureException { if (settings != null && !settings.isInteractiveMode()) { assureInputInBatchMode(getFunctionName(), - str -> isNotEmpty(str) && isIdentifier(str) && !isKeyword(str), + str -> isNotEmpty(str) && str.matches(FUNCTION_NAME_REGEXP), this::setFunctionName, true); } else { assureInputFromUser("Enter value for Function Name: ", getFunctionName(), - str -> isNotEmpty(str) && isIdentifier(str) && !isKeyword(str), - "Input should be a valid Java class name.", + str -> isNotEmpty(str) && str.matches(FUNCTION_NAME_REGEXP), + "Function name must start with a letter and can contain letters, digits, '_' and '-'", this::setFunctionName); } } @@ -333,7 +339,7 @@ protected File getPackageDir() { } protected File getTargetFile(final File packageDir) throws Exception { - final String functionName = getFunctionName() + ".java"; + final String functionName = getClassName() + ".java"; final File targetFile = new File(packageDir, functionName); if (targetFile.exists()) { throw new FileAlreadyExistsException(format(FILE_EXIST, targetFile.getAbsolutePath())); diff --git a/azure-functions-maven-plugin/src/main/resources/templates.json b/azure-functions-maven-plugin/src/main/resources/templates.json index 83e9c32bb0..84a5bbcc36 100644 --- a/azure-functions-maven-plugin/src/main/resources/templates.json +++ b/azure-functions-maven-plugin/src/main/resources/templates.json @@ -9,7 +9,7 @@ "userPrompt": [] }, "files": { - "function.java": "package $packageName$;\n\nimport java.util.*;\nimport com.microsoft.azure.serverless.functions.annotation.*;\nimport com.microsoft.azure.serverless.functions.*;\n\n/**\n * Azure Functions with HTTP trigger.\n */\npublic class $functionName$ {\n /**\n * This function will listen at HTTP endpoint \"/api/$functionName$\". Two approaches to invoke it using \"curl\" command in bash:\n * 1. curl -d \"Http Body\" {your host}/api/$functionName$\n * 2. curl {your host}/api/$functionName$?name=HTTP%20Query\n */\n @FunctionName(\"$functionName$\")\n public HttpResponseMessage httpHandler(\n @HttpTrigger(name = \"req\", methods = { \"get\", \"post\" }, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request,\n final ExecutionContext context\n ) {\n context.getLogger().info(\"Java HTTP trigger processed a request.\");\n\n // Parse query parameters\n String query = request.getQueryParameters().get(\"name\");\n String name = request.getBody().orElse(query);\n\n if (name == null) {\n return request.createResponse(400, \"Please pass a name on the query string or in the request body\");\n } else {\n return request.createResponse(200, \"Hello, \" + name);\n }\n }\n}\n" + "function.java": "package $packageName$;\n\nimport java.util.*;\nimport com.microsoft.azure.serverless.functions.annotation.*;\nimport com.microsoft.azure.serverless.functions.*;\n\n/**\n * Azure Functions with HTTP trigger.\n */\npublic class $className$ {\n /**\n * This function will listen at HTTP endpoint \"/api/$functionName$\". Two approaches to invoke it using \"curl\" command in bash:\n * 1. curl -d \"Http Body\" {your host}/api/$functionName$\n * 2. curl {your host}/api/$functionName$?name=HTTP%20Query\n */\n @FunctionName(\"$functionName$\")\n public HttpResponseMessage httpHandler(\n @HttpTrigger(name = \"req\", methods = { \"get\", \"post\" }, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request,\n final ExecutionContext context\n ) {\n context.getLogger().info(\"Java HTTP trigger processed a request.\");\n\n // Parse query parameters\n String query = request.getQueryParameters().get(\"name\");\n String name = request.getBody().orElse(query);\n\n if (name == null) {\n return request.createResponse(400, \"Please pass a name on the query string or in the request body\");\n } else {\n return request.createResponse(200, \"Hello, \" + name);\n }\n }\n}\n" } }, { @@ -25,7 +25,7 @@ ] }, "files": { - "function.java": "package $packageName$;\n\nimport com.microsoft.azure.serverless.functions.annotation.*;\nimport com.microsoft.azure.serverless.functions.*;\n\n/**\n * Azure Functions with Azure Blob trigger.\n */\npublic class $functionName$ {\n /**\n * This function will be invoked when a new or updated blob is detected at the specified path. The blob contents are provided as input to this function.\n */\n @FunctionName(\"$functionName$\")\n @StorageAccount(\"$connection$\")\n public void blobHandler(\n @BlobTrigger(name = \"content\", path = \"$path$\", dataType = \"binary\") byte[] content,\n @BindingName(\"name\") String name,\n final ExecutionContext context\n ) {\n context.getLogger().info(\"Java Blob trigger function processed a blob. Name: \" + name + \"\\n Size: \" + content.length + \" Bytes\");\n }\n}\n" + "function.java": "package $packageName$;\n\nimport com.microsoft.azure.serverless.functions.annotation.*;\nimport com.microsoft.azure.serverless.functions.*;\n\n/**\n * Azure Functions with Azure Blob trigger.\n */\npublic class $className$ {\n /**\n * This function will be invoked when a new or updated blob is detected at the specified path. The blob contents are provided as input to this function.\n */\n @FunctionName(\"$functionName$\")\n @StorageAccount(\"$connection$\")\n public void blobHandler(\n @BlobTrigger(name = \"content\", path = \"$path$\", dataType = \"binary\") byte[] content,\n @BindingName(\"name\") String name,\n final ExecutionContext context\n ) {\n context.getLogger().info(\"Java Blob trigger function processed a blob. Name: \" + name + \"\\n Size: \" + content.length + \" Bytes\");\n }\n}\n" } }, { @@ -41,7 +41,7 @@ ] }, "files": { - "function.java": "package $packageName$;\n\nimport com.microsoft.azure.serverless.functions.annotation.*;\nimport com.microsoft.azure.serverless.functions.*;\n\n/**\n * Azure Functions with Azure Storage Queue trigger.\n */\npublic class $functionName$ {\n /**\n * This function will be invoked when a new message is received at the specified path. The message contents are provided as input to this function.\n */\n @FunctionName(\"$functionName$\")\n public void queueHandler(\n @QueueTrigger(name = \"message\", queueName = \"$queueName$\", connection = \"$connection$\") String message,\n final ExecutionContext context\n ) {\n context.getLogger().info(\"Java Queue trigger function processed a message: \" + message);\n }\n}\n" + "function.java": "package $packageName$;\n\nimport com.microsoft.azure.serverless.functions.annotation.*;\nimport com.microsoft.azure.serverless.functions.*;\n\n/**\n * Azure Functions with Azure Storage Queue trigger.\n */\npublic class $className$ {\n /**\n * This function will be invoked when a new message is received at the specified path. The message contents are provided as input to this function.\n */\n @FunctionName(\"$functionName$\")\n public void queueHandler(\n @QueueTrigger(name = \"message\", queueName = \"$queueName$\", connection = \"$connection$\") String message,\n final ExecutionContext context\n ) {\n context.getLogger().info(\"Java Queue trigger function processed a message: \" + message);\n }\n}\n" } }, { @@ -56,7 +56,7 @@ ] }, "files": { - "function.java": "package $packageName$;\n\nimport java.time.*;\nimport com.microsoft.azure.serverless.functions.annotation.*;\nimport com.microsoft.azure.serverless.functions.*;\n\n/**\n * Azure Functions with Timer trigger.\n */\npublic class $functionName$ {\n /**\n * This function will be invoked periodically according to the specified schedule.\n */\n @FunctionName(\"$functionName$\")\n public void timerHandler(\n @TimerTrigger(name = \"timerInfo\", schedule = \"$schedule$\") String timerInfo,\n final ExecutionContext context\n ) {\n context.getLogger().info(\"Java Timer trigger function executed at: \" + LocalDateTime.now());\n }\n}\n" + "function.java": "package $packageName$;\n\nimport java.time.*;\nimport com.microsoft.azure.serverless.functions.annotation.*;\nimport com.microsoft.azure.serverless.functions.*;\n\n/**\n * Azure Functions with Timer trigger.\n */\npublic class $className$ {\n /**\n * This function will be invoked periodically according to the specified schedule.\n */\n @FunctionName(\"$functionName$\")\n public void timerHandler(\n @TimerTrigger(name = \"timerInfo\", schedule = \"$schedule$\") String timerInfo,\n final ExecutionContext context\n ) {\n context.getLogger().info(\"Java Timer trigger function executed at: \" + LocalDateTime.now());\n }\n}\n" } } ] diff --git a/azure-functions-maven-plugin/src/test/java/com/microsoft/azure/maven/function/AddMojoTest.java b/azure-functions-maven-plugin/src/test/java/com/microsoft/azure/maven/function/AddMojoTest.java index 726a78ccb6..5e7abb9c29 100644 --- a/azure-functions-maven-plugin/src/test/java/com/microsoft/azure/maven/function/AddMojoTest.java +++ b/azure-functions-maven-plugin/src/test/java/com/microsoft/azure/maven/function/AddMojoTest.java @@ -53,6 +53,36 @@ public void doExecute() throws Exception { assertTrue(newFunctionFile.exists()); } + @Test(expected = MojoFailureException.class) + public void doExecuteWithInvalidFunctionName() throws Exception { + final AddMojo mojo = getMojoFromPom(); + final Settings settings = new Settings(); + settings.setInteractiveMode(false); + ReflectionUtils.setVariableValueInObject(mojo, "basedir", new File("target/test")); + ReflectionUtils.setVariableValueInObject(mojo, "settings", settings); + mojo.setFunctionTemplate("HttpTrigger"); + mojo.setFunctionName("$NewFunction"); + mojo.setFunctionPackageName("com.microsoft.azure"); + + mojo.doExecute(); + } + + @Test + public void doExecuteWithSpecialFunctionName() throws Exception { + final AddMojo mojo = getMojoFromPom(); + ReflectionUtils.setVariableValueInObject(mojo, "basedir", new File("target/test")); + mojo.setFunctionTemplate("HttpTrigger"); + mojo.setFunctionName("New-Function"); + mojo.setFunctionPackageName("com.microsoft.azure"); + + final File newFunctionFile = new File("target/test/src/main/java/com/microsoft/azure/New_Function.java"); + newFunctionFile.delete(); + + mojo.doExecute(); + + assertTrue(newFunctionFile.exists()); + } + @Test public void assureInputFromUser() throws Exception { final AddMojo mojo = getMojoFromPom();