From a0303817b8d593599a1d39e1805093f5e2b2f274 Mon Sep 17 00:00:00 2001 From: Rafal Wilinski Date: Fri, 1 Sep 2017 19:15:33 +0200 Subject: [PATCH 01/56] Invoke local java functionality --- .gitignore | 1 + lib/plugins/aws/invokeLocal/Invoke.java | 68 +++++++++++++++++++++++++ lib/plugins/aws/invokeLocal/index.js | 42 +++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 lib/plugins/aws/invokeLocal/Invoke.java diff --git a/.gitignore b/.gitignore index b49e4e927..5a7613c76 100755 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,4 @@ tracking-config.json # Misc jest +lib/plugins/aws/invokeLocal/Invoke.class diff --git a/lib/plugins/aws/invokeLocal/Invoke.java b/lib/plugins/aws/invokeLocal/Invoke.java new file mode 100644 index 000000000..fd54d3720 --- /dev/null +++ b/lib/plugins/aws/invokeLocal/Invoke.java @@ -0,0 +1,68 @@ +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.Class; +import java.lang.reflect.Type; +import java.net.URL; +import java.net.URLClassLoader; +import java.net.MalformedURLException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.io.BufferedReader; + +public class Invoke { + private File artifact; + private String className; + private Object instance; + private Class clazz; + + public Invoke() { + this.artifact = new File(new File("."), System.getProperty("artifactPath")); + this.className = System.getProperty("className"); + + try { + HashMap parsedInput = new HashMap<>(); + String input = getInput(); + + // parsedInput = this.parseInput(input); - should parse String -> Json -> Map + // Context - no ideas... + + this.instance = this.getInstance(); + this.invoke(new HashMap(), null); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private Object getInstance() throws Exception { + URL[] urls = {this.artifact.toURI().toURL()}; + URLClassLoader child = new URLClassLoader(urls, this.getClass().getClassLoader()); + + this.clazz = Class.forName(this.className, true, child); + + return this.clazz.newInstance(); + } + + private Object invoke(HashMap event, Object context) throws Exception { + Method[] methods = this.clazz.getDeclaredMethods(); + + return methods[1].invoke(this.instance, event, context); + } + + private String getInput() throws IOException { + BufferedReader streamReader = new BufferedReader(new InputStreamReader(System.in, "UTF-8")); + StringBuilder inputStringBuilder = new StringBuilder(); + String inputStr; + + while ((inputStr = streamReader.readLine()) != null) { + inputStringBuilder.append(inputStr); + } + + return inputStringBuilder.toString(); + } + + public static void main(String[] args) { + new Invoke(); + } +} diff --git a/lib/plugins/aws/invokeLocal/index.js b/lib/plugins/aws/invokeLocal/index.js index 927c212a1..09c088751 100644 --- a/lib/plugins/aws/invokeLocal/index.js +++ b/lib/plugins/aws/invokeLocal/index.js @@ -136,6 +136,15 @@ class AwsInvokeLocal { this.options.context); } + if (runtime === 'java8') { + return this.invokeLocalJava( + 'java', + handler, + this.serverless.service.package.artifact, + this.options.data, + this.options.context); + } + throw new this.serverless.classes .Error('You can only invoke Node.js & Python functions locally.'); } @@ -161,6 +170,39 @@ class AwsInvokeLocal { }); } + invokeLocalJava(runtime, className, artifactPath, event, context) { + const input = JSON.stringify({ + event: event || {}, + context, + }); + + if (process.env.VIRTUAL_ENV) { + process.env.PATH = `${process.env.VIRTUAL_ENV}/bin:${process.env.PATH}`; + } + + return new BbPromise(resolve => { + const javac = spawn('javac', [path.join(__dirname, 'Invoke.java')]); + + javac.stderr.on('data', (buf) => this.serverless.cli.consoleLog(`javac - ${buf.toString()}`)); + javac.stdin.end(); + javac.on('close', () => { + const java = spawn('java', [ + `-DartifactPath=${artifactPath}`, + `-DclassName=${className}`, + '-cp', + __dirname, + 'Invoke', + ]); + + java.stdout.on('data', (buf) => this.serverless.cli.consoleLog(buf.toString())); + java.stderr.on('data', (buf) => this.serverless.cli.consoleLog(buf.toString())); + java.stdin.write(input); + java.stdin.end(); + java.on('close', () => resolve()); + }); + }); + } + invokeLocalNodeJs(handlerPath, handlerName, event, customContext) { let lambda; From faf696b77da0846657db569d4096bf255c533202 Mon Sep 17 00:00:00 2001 From: Rafal Wilinski Date: Sat, 2 Sep 2017 15:45:31 +0200 Subject: [PATCH 02/56] Add maven as dependency manager --- .gitignore | 2 +- lib/plugins/aws/invokeLocal/index.js | 62 ++++++++++------- lib/plugins/aws/invokeLocal/java/MANIFEST.mf | 2 + lib/plugins/aws/invokeLocal/java/pom.xml | 66 +++++++++++++++++++ .../src/main/java/com/serverless/Context.java | 51 ++++++++++++++ .../src/main/java/com/serverless}/Invoke.java | 36 ++++++---- 6 files changed, 181 insertions(+), 38 deletions(-) create mode 100644 lib/plugins/aws/invokeLocal/java/MANIFEST.mf create mode 100644 lib/plugins/aws/invokeLocal/java/pom.xml create mode 100644 lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/Context.java rename lib/plugins/aws/invokeLocal/{ => java/src/main/java/com/serverless}/Invoke.java (64%) diff --git a/.gitignore b/.gitignore index 5a7613c76..52fb14cbd 100755 --- a/.gitignore +++ b/.gitignore @@ -48,4 +48,4 @@ tracking-config.json # Misc jest -lib/plugins/aws/invokeLocal/Invoke.class +lib/plugins/aws/invokeLocal/java/target diff --git a/lib/plugins/aws/invokeLocal/index.js b/lib/plugins/aws/invokeLocal/index.js index 09c088751..10212d979 100644 --- a/lib/plugins/aws/invokeLocal/index.js +++ b/lib/plugins/aws/invokeLocal/index.js @@ -141,8 +141,7 @@ class AwsInvokeLocal { 'java', handler, this.serverless.service.package.artifact, - this.options.data, - this.options.context); + this.options.data); } throw new this.serverless.classes @@ -170,36 +169,53 @@ class AwsInvokeLocal { }); } - invokeLocalJava(runtime, className, artifactPath, event, context) { - const input = JSON.stringify({ - event: event || {}, - context, + callJavaBridge(artifactPath, className, input) { + return new BbPromise((resolve) => { + const java = spawn('java', [ + `-DartifactPath=${artifactPath}`, + `-DclassName=${className}`, + '-jar', + path.join(__dirname, 'java', 'target', 'invoke-bridge-1.0.jar'), + ]); + + java.stdout.on('data', (buf) => this.serverless.cli.consoleLog(buf.toString())); + java.stderr.on('data', (buf) => this.serverless.cli.consoleLog(buf.toString())); + java.stdin.write(input); + java.stdin.end(); + java.on('close', () => resolve()); }); + } + + invokeLocalJava(runtime, className, artifactPath, event) { + const input = JSON.stringify(event || {}); + const javaBridgePath = path.join(__dirname, 'java'); + const executablePath = path.join(javaBridgePath, 'target'); if (process.env.VIRTUAL_ENV) { process.env.PATH = `${process.env.VIRTUAL_ENV}/bin:${process.env.PATH}`; } return new BbPromise(resolve => { - const javac = spawn('javac', [path.join(__dirname, 'Invoke.java')]); - - javac.stderr.on('data', (buf) => this.serverless.cli.consoleLog(`javac - ${buf.toString()}`)); - javac.stdin.end(); - javac.on('close', () => { - const java = spawn('java', [ - `-DartifactPath=${artifactPath}`, - `-DclassName=${className}`, - '-cp', - __dirname, - 'Invoke', + if (!this.serverless.utils.dirExistsSync(executablePath)) { + const mvn = spawn('mvn', [ + 'package', + '-f', + javaBridgePath, ]); - java.stdout.on('data', (buf) => this.serverless.cli.consoleLog(buf.toString())); - java.stderr.on('data', (buf) => this.serverless.cli.consoleLog(buf.toString())); - java.stdin.write(input); - java.stdin.end(); - java.on('close', () => resolve()); - }); + this.serverless.cli.consoleLog( + 'Building Java bridge, first invocation might take a bit longer.' + ); + + mvn.stderr.on('data', (buf) => this.serverless.cli.consoleLog(`mvn - ${buf.toString()}`)); + mvn.stdin.end(); + + mvn.on('close', () => { + this.callJavaBridge(artifactPath, className, input).then(resolve); + }); + } else { + this.callJavaBridge(artifactPath, className, input).then(resolve); + } }); } diff --git a/lib/plugins/aws/invokeLocal/java/MANIFEST.mf b/lib/plugins/aws/invokeLocal/java/MANIFEST.mf new file mode 100644 index 000000000..13a041fef --- /dev/null +++ b/lib/plugins/aws/invokeLocal/java/MANIFEST.mf @@ -0,0 +1,2 @@ +Manifest-version: 1.0 +Main-Class: com.serverless.Invoke diff --git a/lib/plugins/aws/invokeLocal/java/pom.xml b/lib/plugins/aws/invokeLocal/java/pom.xml new file mode 100644 index 000000000..5c225a212 --- /dev/null +++ b/lib/plugins/aws/invokeLocal/java/pom.xml @@ -0,0 +1,66 @@ + + + 4.0.0 + + com.serverless + invoke-bridge + 1.0 + + + + com.amazonaws + aws-lambda-java-core + 1.1.0 + + + com.amazonaws + aws-lambda-java-log4j + 1.0.0 + + + com.fasterxml.jackson.core + jackson-core + 2.8.5 + + + com.fasterxml.jackson.core + jackson-databind + 2.8.5 + + + com.fasterxml.jackson.core + jackson-annotations + 2.8.5 + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 2.3 + + false + + + + package + + shade + + + + + com.serverless.Invoke + + + + + + + + + diff --git a/lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/Context.java b/lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/Context.java new file mode 100644 index 000000000..13fc5cec0 --- /dev/null +++ b/lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/Context.java @@ -0,0 +1,51 @@ +package com.serverless; + +import com.amazonaws.services.lambda.runtime.ClientContext; +import com.amazonaws.services.lambda.runtime.CognitoIdentity; +import com.amazonaws.services.lambda.runtime.LambdaLogger; + +public class Context implements com.amazonaws.services.lambda.runtime.Context { + public String getAwsRequestId() { + return null; + } + + public String getLogGroupName() { + return null; + } + + public String getLogStreamName() { + return null; + } + + public String getFunctionName() { + return null; + } + + public String getFunctionVersion() { + return null; + } + + public String getInvokedFunctionArn() { + return null; + } + + public CognitoIdentity getIdentity() { + return null; + } + + public ClientContext getClientContext() { + return null; + } + + public int getRemainingTimeInMillis() { + return 0; + } + + public int getMemoryLimitInMB() { + return 0; + } + + public LambdaLogger getLogger() { + return null; + } +} diff --git a/lib/plugins/aws/invokeLocal/Invoke.java b/lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/Invoke.java similarity index 64% rename from lib/plugins/aws/invokeLocal/Invoke.java rename to lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/Invoke.java index fd54d3720..b6bc348b2 100644 --- a/lib/plugins/aws/invokeLocal/Invoke.java +++ b/lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/Invoke.java @@ -1,15 +1,17 @@ +package com.serverless; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; -import java.lang.Class; -import java.lang.reflect.Type; +import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; -import java.net.MalformedURLException; -import java.lang.reflect.Method; import java.util.HashMap; -import java.util.Map; -import java.io.BufferedReader; public class Invoke { private File artifact; @@ -17,19 +19,16 @@ public class Invoke { private Object instance; private Class clazz; - public Invoke() { + private Invoke() { this.artifact = new File(new File("."), System.getProperty("artifactPath")); this.className = System.getProperty("className"); try { - HashMap parsedInput = new HashMap<>(); - String input = getInput(); - - // parsedInput = this.parseInput(input); - should parse String -> Json -> Map - // Context - no ideas... + HashMap parsedInput = parseInput(getInput()); + System.out.println(getInput()); this.instance = this.getInstance(); - this.invoke(new HashMap(), null); + System.out.println(this.invoke(parsedInput, new Context())); } catch (Exception e) { e.printStackTrace(); } @@ -44,12 +43,21 @@ public class Invoke { return this.clazz.newInstance(); } - private Object invoke(HashMap event, Object context) throws Exception { + private Object invoke(HashMap event, Context context) throws Exception { Method[] methods = this.clazz.getDeclaredMethods(); return methods[1].invoke(this.instance, event, context); } + private HashMap parseInput(String input) throws IOException { + TypeReference> typeRef = new TypeReference>() {}; + ObjectMapper mapper = new ObjectMapper(); + + JsonNode jsonNode = mapper.readTree(input); + + return mapper.convertValue(jsonNode, typeRef); + } + private String getInput() throws IOException { BufferedReader streamReader = new BufferedReader(new InputStreamReader(System.in, "UTF-8")); StringBuilder inputStringBuilder = new StringBuilder(); From c9bb961fd00a7d2608d85779c106ef326c04755b Mon Sep 17 00:00:00 2001 From: Rafal Wilinski Date: Sat, 2 Sep 2017 15:46:32 +0200 Subject: [PATCH 03/56] Remove unnecessary code --- .../invokeLocal/java/src/main/java/com/serverless/Invoke.java | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/Invoke.java b/lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/Invoke.java index b6bc348b2..ab381abdf 100644 --- a/lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/Invoke.java +++ b/lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/Invoke.java @@ -25,7 +25,6 @@ public class Invoke { try { HashMap parsedInput = parseInput(getInput()); - System.out.println(getInput()); this.instance = this.getInstance(); System.out.println(this.invoke(parsedInput, new Context())); From 3cf32ef60ac48f10e68f591ba348f2edf568e17e Mon Sep 17 00:00:00 2001 From: Rafal Wilinski Date: Sat, 2 Sep 2017 22:02:16 +0200 Subject: [PATCH 04/56] Add custom context passing --- lib/plugins/aws/invokeLocal/index.js | 10 +++- lib/plugins/aws/invokeLocal/java/MANIFEST.mf | 2 +- lib/plugins/aws/invokeLocal/java/pom.xml | 2 +- .../src/main/java/com/serverless/Context.java | 55 +++++++++++++++---- .../{Invoke.java => InvokeBridge.java} | 20 +++++-- 5 files changed, 69 insertions(+), 20 deletions(-) rename lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/{Invoke.java => InvokeBridge.java} (74%) diff --git a/lib/plugins/aws/invokeLocal/index.js b/lib/plugins/aws/invokeLocal/index.js index 10212d979..d961c34cd 100644 --- a/lib/plugins/aws/invokeLocal/index.js +++ b/lib/plugins/aws/invokeLocal/index.js @@ -141,7 +141,8 @@ class AwsInvokeLocal { 'java', handler, this.serverless.service.package.artifact, - this.options.data); + this.options.data, + this.options.context); } throw new this.serverless.classes @@ -186,10 +187,13 @@ class AwsInvokeLocal { }); } - invokeLocalJava(runtime, className, artifactPath, event) { - const input = JSON.stringify(event || {}); + invokeLocalJava(runtime, className, artifactPath, event, context) { const javaBridgePath = path.join(__dirname, 'java'); const executablePath = path.join(javaBridgePath, 'target'); + const input = JSON.stringify({ + event: event || {}, + context: context || {}, + }); if (process.env.VIRTUAL_ENV) { process.env.PATH = `${process.env.VIRTUAL_ENV}/bin:${process.env.PATH}`; diff --git a/lib/plugins/aws/invokeLocal/java/MANIFEST.mf b/lib/plugins/aws/invokeLocal/java/MANIFEST.mf index 13a041fef..3cc01de12 100644 --- a/lib/plugins/aws/invokeLocal/java/MANIFEST.mf +++ b/lib/plugins/aws/invokeLocal/java/MANIFEST.mf @@ -1,2 +1,2 @@ Manifest-version: 1.0 -Main-Class: com.serverless.Invoke +Main-Class: com.serverless.InvokeBridge diff --git a/lib/plugins/aws/invokeLocal/java/pom.xml b/lib/plugins/aws/invokeLocal/java/pom.xml index 5c225a212..80aa516a9 100644 --- a/lib/plugins/aws/invokeLocal/java/pom.xml +++ b/lib/plugins/aws/invokeLocal/java/pom.xml @@ -54,7 +54,7 @@ - com.serverless.Invoke + com.serverless.InvokeBridge diff --git a/lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/Context.java b/lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/Context.java index 13fc5cec0..035e23cbe 100644 --- a/lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/Context.java +++ b/lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/Context.java @@ -1,51 +1,84 @@ package com.serverless; +import com.amazonaws.services.lambda.runtime.Client; import com.amazonaws.services.lambda.runtime.ClientContext; import com.amazonaws.services.lambda.runtime.CognitoIdentity; import com.amazonaws.services.lambda.runtime.LambdaLogger; +import java.util.Map; + public class Context implements com.amazonaws.services.lambda.runtime.Context { + private String name; + private String version; + private long endTime; + + Context(String name, String version, int timeout) { + this.name = name; + this.version = version; + this.endTime = System.currentTimeMillis() + (timeout * 1000); + } + public String getAwsRequestId() { - return null; + return "1234567890"; } public String getLogGroupName() { - return null; + return "LogGroup_" + this.name; } public String getLogStreamName() { - return null; + return "LogStream_" + this.name; } public String getFunctionName() { - return null; + return this.name; } public String getFunctionVersion() { - return null; + return this.version; } public String getInvokedFunctionArn() { - return null; + return "arn:aws:lambda:serverless:" + this.name; } public CognitoIdentity getIdentity() { - return null; + return new CognitoIdentity() { + public String getIdentityId() { + return "1"; + } + + public String getIdentityPoolId() { + return "1"; + } + }; } public ClientContext getClientContext() { - return null; + return new ClientContext() { + public Client getClient() { + return null; + } + + public Map getCustom() { + return null; + } + + public Map getEnvironment() { + return System.getenv(); + } + }; } public int getRemainingTimeInMillis() { - return 0; + return Math.max(0, (int) (this.endTime - System.currentTimeMillis())); } public int getMemoryLimitInMB() { - return 0; + return 1024; } public LambdaLogger getLogger() { - return null; + return System.out::println; } } diff --git a/lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/Invoke.java b/lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/InvokeBridge.java similarity index 74% rename from lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/Invoke.java rename to lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/InvokeBridge.java index ab381abdf..61e1c05d8 100644 --- a/lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/Invoke.java +++ b/lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/InvokeBridge.java @@ -13,26 +13,38 @@ import java.net.URL; import java.net.URLClassLoader; import java.util.HashMap; -public class Invoke { +public class InvokeBridge { private File artifact; private String className; private Object instance; private Class clazz; - private Invoke() { + private InvokeBridge() { this.artifact = new File(new File("."), System.getProperty("artifactPath")); this.className = System.getProperty("className"); try { HashMap parsedInput = parseInput(getInput()); + HashMap eventMap = (HashMap) parsedInput.get("event"); this.instance = this.getInstance(); - System.out.println(this.invoke(parsedInput, new Context())); + + System.out.println(this.invoke(eventMap, this.getContext(parsedInput)).toString()); } catch (Exception e) { e.printStackTrace(); } } + private Context getContext(HashMap parsedInput) { + HashMap contextMap = (HashMap) parsedInput.get("context"); + + String name = (String) contextMap.getOrDefault("name", "name"); + String version = (String) contextMap.getOrDefault("version", "LATEST"); + int timeout = Integer.parseInt(String.valueOf(contextMap.getOrDefault("timeout", 5))); + + return new Context(name, version, timeout); + } + private Object getInstance() throws Exception { URL[] urls = {this.artifact.toURI().toURL()}; URLClassLoader child = new URLClassLoader(urls, this.getClass().getClassLoader()); @@ -70,6 +82,6 @@ public class Invoke { } public static void main(String[] args) { - new Invoke(); + new InvokeBridge(); } } From b1425c5ad5cbd0ab8b350edfed6b8324319780c6 Mon Sep 17 00:00:00 2001 From: Rafal Wilinski Date: Sat, 2 Sep 2017 22:11:04 +0200 Subject: [PATCH 05/56] Context related fixes --- lib/plugins/aws/invokeLocal/index.js | 20 ++++++++++++------- .../src/main/java/com/serverless/Context.java | 6 ++++-- .../java/com/serverless/InvokeBridge.java | 5 +++-- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/lib/plugins/aws/invokeLocal/index.js b/lib/plugins/aws/invokeLocal/index.js index d961c34cd..4107e2ced 100644 --- a/lib/plugins/aws/invokeLocal/index.js +++ b/lib/plugins/aws/invokeLocal/index.js @@ -187,17 +187,23 @@ class AwsInvokeLocal { }); } - invokeLocalJava(runtime, className, artifactPath, event, context) { - const javaBridgePath = path.join(__dirname, 'java'); - const executablePath = path.join(javaBridgePath, 'target'); + invokeLocalJava(runtime, className, artifactPath, event, customContext) { + const timeout = Number(this.options.functionObj.timeout) + || Number(this.serverless.service.provider.timeout) + || 6; + const context = { + name: this.options.functionObj.name, + version: 'LATEST', + logGroupName: this.provider.naming.getLogGroupName(this.options.functionObj.name), + timeout, + }; const input = JSON.stringify({ event: event || {}, - context: context || {}, + context: customContext || context, }); - if (process.env.VIRTUAL_ENV) { - process.env.PATH = `${process.env.VIRTUAL_ENV}/bin:${process.env.PATH}`; - } + const javaBridgePath = path.join(__dirname, 'java'); + const executablePath = path.join(javaBridgePath, 'target'); return new BbPromise(resolve => { if (!this.serverless.utils.dirExistsSync(executablePath)) { diff --git a/lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/Context.java b/lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/Context.java index 035e23cbe..b0c93945c 100644 --- a/lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/Context.java +++ b/lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/Context.java @@ -10,11 +10,13 @@ import java.util.Map; public class Context implements com.amazonaws.services.lambda.runtime.Context { private String name; private String version; + private String logGroupName; private long endTime; - Context(String name, String version, int timeout) { + Context(String name, String version, String logGroupName, int timeout) { this.name = name; this.version = version; + this.logGroupName = logGroupName; this.endTime = System.currentTimeMillis() + (timeout * 1000); } @@ -23,7 +25,7 @@ public class Context implements com.amazonaws.services.lambda.runtime.Context { } public String getLogGroupName() { - return "LogGroup_" + this.name; + return this.logGroupName; } public String getLogStreamName() { diff --git a/lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/InvokeBridge.java b/lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/InvokeBridge.java index 61e1c05d8..ab9629426 100644 --- a/lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/InvokeBridge.java +++ b/lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/InvokeBridge.java @@ -38,11 +38,12 @@ public class InvokeBridge { private Context getContext(HashMap parsedInput) { HashMap contextMap = (HashMap) parsedInput.get("context"); - String name = (String) contextMap.getOrDefault("name", "name"); + String name = (String) contextMap.getOrDefault("name", "functionName"); String version = (String) contextMap.getOrDefault("version", "LATEST"); + String logGroupName = (String) contextMap.getOrDefault("logGroupName", "logGroup"); int timeout = Integer.parseInt(String.valueOf(contextMap.getOrDefault("timeout", 5))); - return new Context(name, version, timeout); + return new Context(name, version, logGroupName, timeout); } private Object getInstance() throws Exception { From 1b03a43259b8aa269bf83e83abeca577bbc390ba Mon Sep 17 00:00:00 2001 From: Rafal Wilinski Date: Mon, 4 Sep 2017 12:08:48 +0200 Subject: [PATCH 06/56] Revert encoding change --- lib/plugins/package/lib/zipService.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugins/package/lib/zipService.js b/lib/plugins/package/lib/zipService.js index fd0476fe0..a1354f16f 100644 --- a/lib/plugins/package/lib/zipService.js +++ b/lib/plugins/package/lib/zipService.js @@ -101,7 +101,7 @@ module.exports = { }, getFileContent(fullPath) { - return fs.readFileAsync(fullPath); + return fs.readFileAsync(fullPath, 'utf8'); }, }; From 612cec59c547248350a12f73b5a4d0b7dd8f7bba Mon Sep 17 00:00:00 2001 From: Rafal Wilinski Date: Mon, 4 Sep 2017 16:24:41 +0200 Subject: [PATCH 07/56] Fix unit test --- lib/plugins/aws/invokeLocal/index.test.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/plugins/aws/invokeLocal/index.test.js b/lib/plugins/aws/invokeLocal/index.test.js index 48414c278..87e938e07 100644 --- a/lib/plugins/aws/invokeLocal/index.test.js +++ b/lib/plugins/aws/invokeLocal/index.test.js @@ -276,12 +276,15 @@ describe('AwsInvokeLocal', () => { describe('#invokeLocal()', () => { let invokeLocalNodeJsStub; let invokeLocalPythonStub; + let invokeLocalJavaStub; beforeEach(() => { invokeLocalNodeJsStub = sinon.stub(awsInvokeLocal, 'invokeLocalNodeJs').resolves(); invokeLocalPythonStub = sinon.stub(awsInvokeLocal, 'invokeLocalPython').resolves(); + invokeLocalJavaStub = + sinon.stub(awsInvokeLocal, 'invokeLocalJava').resolves(); awsInvokeLocal.serverless.service.service = 'new-service'; awsInvokeLocal.options = { @@ -298,6 +301,7 @@ describe('AwsInvokeLocal', () => { afterEach(() => { invokeLocalNodeJsStub.restore(); invokeLocalPythonStub.restore(); + invokeLocalJavaStub.restore(); }); it('should call invokeLocalNodeJs when no runtime is set', () => awsInvokeLocal.invokeLocal() @@ -355,11 +359,26 @@ describe('AwsInvokeLocal', () => { )).to.be.equal(true); awsInvokeLocal.invokeLocalPython.restore(); }); + }); + + it('should call invokeLocalJava when java8 runtime is set', () => { + awsInvokeLocal.options.functionObj.runtime = 'java8'; + awsInvokeLocal.invokeLocal() + .then(() => { + expect(invokeLocalJavaStub.calledOnce).to.be.equal(true); + expect(invokeLocalJavaStub.calledWithExactly( + 'java8', + 'handler', + 'hello', + {} + )).to.be.equal(true); + awsInvokeLocal.invokeLocalJava.restore(); + }); delete awsInvokeLocal.options.functionObj.runtime; }); it('throw error when using runtime other than Node.js or Python', () => { - awsInvokeLocal.options.functionObj.runtime = 'java8'; + awsInvokeLocal.options.functionObj.runtime = 'go'; expect(() => awsInvokeLocal.invokeLocal()).to.throw(Error); delete awsInvokeLocal.options.functionObj.runtime; }); From 0423c6e22867f50f933c72a2be3fe00418e23b93 Mon Sep 17 00:00:00 2001 From: Rafal Wilinski Date: Wed, 6 Sep 2017 12:43:34 +0200 Subject: [PATCH 08/56] Improve tests coverage, add documentation --- .../aws/cli-reference/invoke-local.md | 4 +- lib/plugins/aws/invokeLocal/index.js | 2 +- lib/plugins/aws/invokeLocal/index.test.js | 209 +++++++++++++++++- 3 files changed, 202 insertions(+), 13 deletions(-) diff --git a/docs/providers/aws/cli-reference/invoke-local.md b/docs/providers/aws/cli-reference/invoke-local.md index e52daead2..20a618a98 100644 --- a/docs/providers/aws/cli-reference/invoke-local.md +++ b/docs/providers/aws/cli-reference/invoke-local.md @@ -96,7 +96,9 @@ This example will pass the json context in the `lib/context.json` file (relative ### Limitations -Currently, `invoke local` only supports the NodeJs and Python runtimes. +Currently, `invoke local` only supports the NodeJs, Python & Java runtimes. + +**Note:** In order to get correct output when using Java runtime, your Response class must implement `toString()` method. ## Resource permissions diff --git a/lib/plugins/aws/invokeLocal/index.js b/lib/plugins/aws/invokeLocal/index.js index 4107e2ced..d1064a388 100644 --- a/lib/plugins/aws/invokeLocal/index.js +++ b/lib/plugins/aws/invokeLocal/index.js @@ -146,7 +146,7 @@ class AwsInvokeLocal { } throw new this.serverless.classes - .Error('You can only invoke Node.js & Python functions locally.'); + .Error('You can only invoke Node.js, Python & Java functions locally.'); } invokeLocalPython(runtime, handlerPath, handlerName, event, context) { diff --git a/lib/plugins/aws/invokeLocal/index.test.js b/lib/plugins/aws/invokeLocal/index.test.js index 2d2a0356f..796cfd630 100644 --- a/lib/plugins/aws/invokeLocal/index.test.js +++ b/lib/plugins/aws/invokeLocal/index.test.js @@ -3,6 +3,10 @@ const expect = require('chai').expect; const sinon = require('sinon'); const path = require('path'); +const BbPromise = require('bluebird'); +const mockRequire = require('mock-require'); +const EventEmitter = require('events'); +const fs = BbPromise.promisifyAll(require('graceful-fs')); const AwsInvokeLocal = require('./index'); const AwsProvider = require('../provider/awsProvider'); const Serverless = require('../../../Serverless'); @@ -299,9 +303,9 @@ describe('AwsInvokeLocal', () => { }); afterEach(() => { - awsInvokeLocal.invokeLocalNodeJsStub.restore(); - awsInvokeLocal.invokeLocalPythonStub.restore(); - awsInvokeLocal.invokeLocalJavaStub.restore(); + awsInvokeLocal.invokeLocalNodeJs.restore(); + awsInvokeLocal.invokeLocalPython.restore(); + awsInvokeLocal.invokeLocalJava.restore(); }); it('should call invokeLocalNodeJs when no runtime is set', () => awsInvokeLocal.invokeLocal() @@ -356,24 +360,22 @@ describe('AwsInvokeLocal', () => { {}, undefined )).to.be.equal(true); - delete awsInvokeLocal.options.functionObj.runtime; }); }); it('should call invokeLocalJava when java8 runtime is set', () => { awsInvokeLocal.options.functionObj.runtime = 'java8'; - awsInvokeLocal.invokeLocal() + return awsInvokeLocal.invokeLocal() .then(() => { expect(invokeLocalJavaStub.calledOnce).to.be.equal(true); expect(invokeLocalJavaStub.calledWithExactly( - 'java8', - 'handler', - 'hello', - {} + 'java', + 'handler.hello', + undefined, + {}, + undefined )).to.be.equal(true); - awsInvokeLocal.invokeLocalJava.restore(); }); - delete awsInvokeLocal.options.functionObj.runtime; }); it('throw error when using runtime other than Node.js or Python', () => { @@ -527,4 +529,189 @@ describe('AwsInvokeLocal', () => { }); }); }); + + describe('#callJavaBridge', () => { + let awsInvokeLocalMocked; + let writeChildStub; + let endChildStub; + + beforeEach(() => { + writeChildStub = sinon.stub(); + endChildStub = sinon.stub(); + + mockRequire('child_process', { + spawn: () => ({ + stderr: new EventEmitter().on('data', () => {}), + stdout: new EventEmitter().on('data', () => {}), + stdin: { + write: writeChildStub, + end: endChildStub, + }, + on: (key, callback) => callback(), + }), + }); + + // Remove Node.js internal "require cache" contents and re-require ./index.js + delete require.cache[require.resolve('./index')]; + delete require.cache[require.resolve('child_process')]; + + const AwsInvokeLocalMocked = require('./index'); // eslint-disable-line global-require + + serverless.setProvider('aws', new AwsProvider(serverless)); + awsInvokeLocalMocked = new AwsInvokeLocalMocked(serverless, options); + + awsInvokeLocalMocked.options = { + stage: 'dev', + function: 'first', + functionObj: { + handler: 'handler.hello', + name: 'hello', + timeout: 4, + }, + data: {}, + }; + }); + + afterEach(() => { + delete require.cache[require.resolve('./index')]; + delete require.cache[require.resolve('child_process')]; + }); + + it('spawns java process with correct arguments', () => + awsInvokeLocalMocked.callJavaBridge( + __dirname, + 'com.serverless.Handler', + '{}' + ).then(() => { + expect(writeChildStub.calledOnce).to.be.equal(true); + expect(endChildStub.calledOnce).to.be.equal(true); + expect(writeChildStub.calledWithExactly('{}')).to.be.equal(true); + }) + ); + }); + + describe('#invokeLocalJava', () => { + let callJavaBridgeStub; + + beforeEach(() => { + callJavaBridgeStub = sinon.stub(awsInvokeLocal, 'callJavaBridge').resolves(); + awsInvokeLocal.options = { + stage: 'dev', + function: 'first', + functionObj: { + handler: 'handler.hello', + name: 'hello', + timeout: 4, + }, + data: {}, + }; + + serverless.cli = new CLI(serverless); + sinon.stub(serverless.cli, 'consoleLog'); + }); + + afterEach(() => { + serverless.cli.consoleLog.restore(); + callJavaBridgeStub.restore(); + }); + + it('should invoke callJavaBridge when bridge is built', () => { + const bridgePath = path.join(__dirname, 'java', 'target'); + fs.mkdir(bridgePath); + + awsInvokeLocal.invokeLocalJava( + 'java', + 'com.serverless.Handler', + __dirname, + {} + ).then(() => { + expect(callJavaBridgeStub.calledOnce).to.be.equal(true); + expect(callJavaBridgeStub.calledWithExactly( + __dirname, + 'com.serverless.Handler', + JSON.stringify({ + event: {}, + context: { + name: 'hello', + version: 'LATEST', + logGroupName: '/aws/lambda/hello', + timeout: 4, + }, + }) + )).to.be.equal(true); + fs.rmdirSync(bridgePath); + }); + }); + + describe('should build Java bridge', () => { + let awsInvokeLocalMocked; + let callJavaBridgeMockedStub; + + beforeEach(() => { + mockRequire('child_process', { + spawn: () => ({ + stderr: new EventEmitter().on('data', () => {}), + stdout: new EventEmitter().on('data', () => {}), + stdin: { + write: () => {}, + end: () => {}, + }, + on: (key, callback) => callback(), + }), + }); + + // Remove Node.js internal "require cache" contents and re-require ./index.js + delete require.cache[require.resolve('./index')]; + delete require.cache[require.resolve('child_process')]; + + const AwsInvokeLocalMocked = require('./index'); // eslint-disable-line global-require + + serverless.setProvider('aws', new AwsProvider(serverless)); + awsInvokeLocalMocked = new AwsInvokeLocalMocked(serverless, options); + callJavaBridgeMockedStub = sinon.stub(awsInvokeLocalMocked, 'callJavaBridge').resolves(); + + awsInvokeLocalMocked.options = { + stage: 'dev', + function: 'first', + functionObj: { + handler: 'handler.hello', + name: 'hello', + timeout: 4, + }, + data: {}, + }; + }); + + afterEach(() => { + callJavaBridgeMockedStub.restore(); + delete require.cache[require.resolve('./index')]; + delete require.cache[require.resolve('child_process')]; + }); + + it('if it\'s not present yet', () => + awsInvokeLocalMocked.invokeLocalJava( + 'java', + 'com.serverless.Handler', + __dirname, + {} + ).then(() => { + expect(serverless.cli.consoleLog.lastCall.args[0]).to.contain('Building Java bridge, first invocation might take a bit longer.'); //eslint-disable-line + expect(callJavaBridgeMockedStub.calledOnce).to.be.equal(true); + expect(callJavaBridgeMockedStub.calledWithExactly( + __dirname, + 'com.serverless.Handler', + JSON.stringify({ + event: {}, + context: { + name: 'hello', + version: 'LATEST', + logGroupName: '/aws/lambda/hello', + timeout: 4, + }, + }) + )).to.be.equal(true); + }) + ); + }); + }); }); From c184f36e8c6999b1f455f878288406bb79dd86ca Mon Sep 17 00:00:00 2001 From: Scott Ewart Date: Thu, 3 Aug 2017 17:50:33 +0200 Subject: [PATCH 09/56] Upload function artifacts with limited concurrency --- lib/plugins/aws/deploy/lib/uploadArtifacts.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/plugins/aws/deploy/lib/uploadArtifacts.js b/lib/plugins/aws/deploy/lib/uploadArtifacts.js index 380fcea1e..b94f93d78 100644 --- a/lib/plugins/aws/deploy/lib/uploadArtifacts.js +++ b/lib/plugins/aws/deploy/lib/uploadArtifacts.js @@ -83,7 +83,7 @@ module.exports = { let shouldUploadService = false; this.serverless.cli.log('Uploading artifacts...'); const functionNames = this.serverless.service.getAllFunctions(); - const uploadPromises = functionNames.map(name => { + return BbPromise.map(functionNames, (name) => { const functionArtifactFileName = this.provider.naming.getFunctionArtifactName(name); const functionObject = this.serverless.service.getFunction(name); functionObject.package = functionObject.package || {}; @@ -100,9 +100,7 @@ module.exports = { return BbPromise.resolve(); } return this.uploadZipFile(artifactFilePath); - }); - - return BbPromise.all(uploadPromises).then(() => { + }, { concurrency: 3 }).then(() => { if (shouldUploadService) { const artifactFileName = this.provider.naming.getServiceArtifactName(); const artifactFilePath = path.join(this.packagePath, artifactFileName); From 43be48ab3cc1caf2386950236b73d4990f2b605a Mon Sep 17 00:00:00 2001 From: Rafal Wilinski Date: Wed, 6 Sep 2017 18:27:39 +0200 Subject: [PATCH 10/56] Add missing maven source & target properties --- lib/plugins/aws/invokeLocal/java/pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/plugins/aws/invokeLocal/java/pom.xml b/lib/plugins/aws/invokeLocal/java/pom.xml index 80aa516a9..c47be034b 100644 --- a/lib/plugins/aws/invokeLocal/java/pom.xml +++ b/lib/plugins/aws/invokeLocal/java/pom.xml @@ -8,6 +8,11 @@ invoke-bridge 1.0 + + 1.8 + 1.8 + + com.amazonaws From 10987dc533efff098fae4b3bf831e73d1371ba9a Mon Sep 17 00:00:00 2001 From: Andre Rabold Date: Wed, 6 Sep 2017 17:26:53 -0700 Subject: [PATCH 11/56] Use S3 `upload` method instead of `putObject` for uploading artifacts --- lib/plugins/aws/deploy/lib/uploadArtifacts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugins/aws/deploy/lib/uploadArtifacts.js b/lib/plugins/aws/deploy/lib/uploadArtifacts.js index b94f93d78..771e7eb01 100644 --- a/lib/plugins/aws/deploy/lib/uploadArtifacts.js +++ b/lib/plugins/aws/deploy/lib/uploadArtifacts.js @@ -73,7 +73,7 @@ module.exports = { } return this.provider.request('S3', - 'putObject', + 'upload', params, this.options.stage, this.options.region); From 192bc74a8cc355ce6cc1af7f0463f2173b3420d2 Mon Sep 17 00:00:00 2001 From: Philipp Muens Date: Fri, 8 Sep 2017 12:47:34 +0200 Subject: [PATCH 12/56] Fix tests --- lib/plugins/aws/deploy/lib/uploadArtifacts.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js b/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js index 059495288..32f353e58 100644 --- a/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js +++ b/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js @@ -179,7 +179,7 @@ describe('uploadArtifacts', () => { expect(readFileSyncStub.calledOnce).to.equal(true); expect(putObjectStub.calledWithExactly( 'S3', - 'putObject', + 'upload', { Bucket: awsDeploy.bucketName, Key: `${awsDeploy.serverless.service.package.artifactDirectoryName}/artifact.zip`, @@ -211,7 +211,7 @@ describe('uploadArtifacts', () => { expect(readFileSyncStub.calledOnce).to.equal(true); expect(putObjectStub.calledWithExactly( 'S3', - 'putObject', + 'upload', { Bucket: awsDeploy.bucketName, Key: `${awsDeploy.serverless.service.package.artifactDirectoryName}/artifact.zip`, From 55c78ba63e8fec381a5c8ed9e3f30aa310dcb945 Mon Sep 17 00:00:00 2001 From: Philipp Muens Date: Fri, 8 Sep 2017 12:57:35 +0200 Subject: [PATCH 13/56] Rename stubs --- lib/plugins/aws/deploy/lib/uploadArtifacts.test.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js b/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js index 32f353e58..79447270d 100644 --- a/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js +++ b/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js @@ -147,13 +147,13 @@ describe('uploadArtifacts', () => { describe('#uploadZipFile()', () => { let readFileSyncStub; - let putObjectStub; + let uploadStub; beforeEach(() => { readFileSyncStub = sinon .stub(fs, 'readFileSync') .returns(); - putObjectStub = sinon + uploadStub = sinon .stub(awsDeploy.provider, 'request') .resolves(); }); @@ -175,9 +175,9 @@ describe('uploadArtifacts', () => { serverless.utils.writeFileSync(artifactFilePath, 'artifact.zip file content'); return awsDeploy.uploadZipFile(artifactFilePath).then(() => { - expect(putObjectStub.calledOnce).to.be.equal(true); + expect(uploadStub.calledOnce).to.be.equal(true); expect(readFileSyncStub.calledOnce).to.equal(true); - expect(putObjectStub.calledWithExactly( + expect(uploadStub.calledWithExactly( 'S3', 'upload', { From d0549eaa28621a70c77353eec3364e0a8f0876c6 Mon Sep 17 00:00:00 2001 From: Philipp Muens Date: Fri, 8 Sep 2017 13:02:54 +0200 Subject: [PATCH 14/56] Update other stubs as well --- lib/plugins/aws/deploy/lib/uploadArtifacts.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js b/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js index 79447270d..2e0d5ea3f 100644 --- a/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js +++ b/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js @@ -207,9 +207,9 @@ describe('uploadArtifacts', () => { }; return awsDeploy.uploadZipFile(artifactFilePath).then(() => { - expect(putObjectStub.calledOnce).to.be.equal(true); + expect(uploadStub.calledOnce).to.be.equal(true); expect(readFileSyncStub.calledOnce).to.equal(true); - expect(putObjectStub.calledWithExactly( + expect(uploadStub.calledWithExactly( 'S3', 'upload', { From 983fb8377d3f72d70e2409c66b5ae49c8f8d6a0c Mon Sep 17 00:00:00 2001 From: Simon Males Date: Fri, 8 Sep 2017 17:47:39 +0200 Subject: [PATCH 15/56] Remove LAMBDA integration for TypeScript sample --- .../create/templates/aws-nodejs-typescript/serverless.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/plugins/create/templates/aws-nodejs-typescript/serverless.yml b/lib/plugins/create/templates/aws-nodejs-typescript/serverless.yml index ec6923fd5..ba0f16154 100644 --- a/lib/plugins/create/templates/aws-nodejs-typescript/serverless.yml +++ b/lib/plugins/create/templates/aws-nodejs-typescript/serverless.yml @@ -19,4 +19,3 @@ functions: - http: method: get path: hello - integration: lambda From 42a7068d18ae8d8d18e3feada0032aa81e6837f9 Mon Sep 17 00:00:00 2001 From: Rafal Wilinski Date: Sat, 9 Sep 2017 14:00:51 +0200 Subject: [PATCH 16/56] Add aws-kotlin-node template --- .../templates/aws-kotlin-node/build.gradle | 31 ++++ .../templates/aws-kotlin-node/gitignore | 15 ++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54783 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + .../create/templates/aws-kotlin-node/gradlew | 172 ++++++++++++++++++ .../templates/aws-kotlin-node/gradlew.bat | 84 +++++++++ .../templates/aws-kotlin-node/package.json | 5 + .../templates/aws-kotlin-node/serverless.yml | 98 ++++++++++ .../com/serverless/ApiGatewayResponse.kt | 42 +++++ .../src/main/kotlin/com/serverless/Handler.kt | 16 ++ .../main/kotlin/com/serverless/Response.kt | 6 + .../aws-kotlin-node/src/test/kotlin/gitkeep | 0 12 files changed, 475 insertions(+) create mode 100644 lib/plugins/create/templates/aws-kotlin-node/build.gradle create mode 100644 lib/plugins/create/templates/aws-kotlin-node/gitignore create mode 100644 lib/plugins/create/templates/aws-kotlin-node/gradle/wrapper/gradle-wrapper.jar create mode 100644 lib/plugins/create/templates/aws-kotlin-node/gradle/wrapper/gradle-wrapper.properties create mode 100755 lib/plugins/create/templates/aws-kotlin-node/gradlew create mode 100755 lib/plugins/create/templates/aws-kotlin-node/gradlew.bat create mode 100644 lib/plugins/create/templates/aws-kotlin-node/package.json create mode 100644 lib/plugins/create/templates/aws-kotlin-node/serverless.yml create mode 100644 lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/ApiGatewayResponse.kt create mode 100644 lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Handler.kt create mode 100644 lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Response.kt create mode 100644 lib/plugins/create/templates/aws-kotlin-node/src/test/kotlin/gitkeep diff --git a/lib/plugins/create/templates/aws-kotlin-node/build.gradle b/lib/plugins/create/templates/aws-kotlin-node/build.gradle new file mode 100644 index 000000000..907c2cf6c --- /dev/null +++ b/lib/plugins/create/templates/aws-kotlin-node/build.gradle @@ -0,0 +1,31 @@ +group 'serverless-kotlin-node' +version '1.0-SNAPSHOT' + +buildscript { + ext.kotlin_version = '1.1.1' + repositories { + mavenCentral() + } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +apply plugin: 'kotlin2js' + +repositories { + mavenCentral() +} + +dependencies { + compile ( + "org.jetbrains.kotlin:kotlin-stdlib-js:$kotlin_version" + ) + +} + +compileKotlin2Js.kotlinOptions { + moduleKind = "commonjs" + outputFile = "build/index.js" +} \ No newline at end of file diff --git a/lib/plugins/create/templates/aws-kotlin-node/gitignore b/lib/plugins/create/templates/aws-kotlin-node/gitignore new file mode 100644 index 000000000..edbd15784 --- /dev/null +++ b/lib/plugins/create/templates/aws-kotlin-node/gitignore @@ -0,0 +1,15 @@ +*.class +target +/bin/ +/.settings/ +.project +.classpath +.gradle +build/ + +# Serverless directories +.serverless% + +# package directories +node_modules +jspm_packages diff --git a/lib/plugins/create/templates/aws-kotlin-node/gradle/wrapper/gradle-wrapper.jar b/lib/plugins/create/templates/aws-kotlin-node/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..a63555747cfe9cd1c75cdd92b461ed1063872144 GIT binary patch literal 54783 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 EAHwza`2YX_ literal 0 HcmV?d00001 diff --git a/lib/plugins/create/templates/aws-kotlin-node/gradle/wrapper/gradle-wrapper.properties b/lib/plugins/create/templates/aws-kotlin-node/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..be3292d1f --- /dev/null +++ b/lib/plugins/create/templates/aws-kotlin-node/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sat Sep 09 11:10:27 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-3.5-bin.zip diff --git a/lib/plugins/create/templates/aws-kotlin-node/gradlew b/lib/plugins/create/templates/aws-kotlin-node/gradlew new file mode 100755 index 000000000..4453ccea3 --- /dev/null +++ b/lib/plugins/create/templates/aws-kotlin-node/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save ( ) { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/lib/plugins/create/templates/aws-kotlin-node/gradlew.bat b/lib/plugins/create/templates/aws-kotlin-node/gradlew.bat new file mode 100755 index 000000000..e95643d6a --- /dev/null +++ b/lib/plugins/create/templates/aws-kotlin-node/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/lib/plugins/create/templates/aws-kotlin-node/package.json b/lib/plugins/create/templates/aws-kotlin-node/package.json new file mode 100644 index 000000000..192e915c2 --- /dev/null +++ b/lib/plugins/create/templates/aws-kotlin-node/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "kotlin": "^1.1.4" + } +} diff --git a/lib/plugins/create/templates/aws-kotlin-node/serverless.yml b/lib/plugins/create/templates/aws-kotlin-node/serverless.yml new file mode 100644 index 000000000..2a453d411 --- /dev/null +++ b/lib/plugins/create/templates/aws-kotlin-node/serverless.yml @@ -0,0 +1,98 @@ +# Welcome to Serverless! +# +# This file is the main config file for your service. +# It's very minimal at this point and uses default values. +# You can always add more config options for more control. +# We've included some commented out config examples here. +# Just uncomment any of them to get that config option. +# +# For full config options, check the docs: +# docs.serverless.com +# +# Happy Coding! + +service: aws-kotlin-node # NOTE: update this with your service name + +# You can pin your service to only deploy with a specific Serverless version +# Check out our docs for more details +# frameworkVersion: "=X.X.X" + +provider: + name: aws + runtime: node6.10 + +# you can overwrite defaults here +# stage: dev +# region: us-east-1 + +# you can add statements to the Lambda function's IAM Role here +# iamRoleStatements: +# - Effect: "Allow" +# Action: +# - "s3:ListBucket" +# Resource: { "Fn::Join" : ["", ["arn:aws:s3:::", { "Ref" : "ServerlessDeploymentBucket" } ] ] } +# - Effect: "Allow" +# Action: +# - "s3:PutObject" +# Resource: +# Fn::Join: +# - "" +# - - "arn:aws:s3:::" +# - "Ref" : "ServerlessDeploymentBucket" +# - "/*" + +# you can define service wide environment variables here +# environment: +# variable1: value1 + +# you can add packaging information here +package: + artifact: build/index.js + +functions: + hello: + handler: Handler + +# The following are a few example events you can configure +# NOTE: Please make sure to change your handler code to work with those events +# Check the event documentation for details +# events: +# - http: +# path: users/create +# method: get +# - s3: ${env:BUCKET} +# - schedule: rate(10 minutes) +# - sns: greeter-topic +# - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000 +# - alexaSkill +# - iot: +# sql: "SELECT * FROM 'some_topic'" +# - cloudwatchEvent: +# event: +# source: +# - "aws.ec2" +# detail-type: +# - "EC2 Instance State-change Notification" +# detail: +# state: +# - pending +# - cloudwatchLog: '/aws/lambda/hello' +# - cognitoUserPool: +# pool: MyUserPool +# trigger: PreSignUp + +# Define function environment variables here +# environment: +# variable2: value2 + +# you can add CloudFormation resource templates here +#resources: +# Resources: +# NewResource: +# Type: AWS::S3::Bucket +# Properties: +# BucketName: my-new-bucket +# Outputs: +# NewOutput: +# Description: "Description for the output" +# Value: "Some output value" \ No newline at end of file diff --git a/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/ApiGatewayResponse.kt b/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/ApiGatewayResponse.kt new file mode 100644 index 000000000..6b50ddbc2 --- /dev/null +++ b/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/ApiGatewayResponse.kt @@ -0,0 +1,42 @@ +class ApiGatewayResponse( + val statusCode: Int = 200, + var body: String? = null, + val headers: Map? = emptyMap(), + val isBase64Encoded: Boolean = false +) { + private constructor(builder: Builder) : this( + builder.statusCode, + builder.rawBody, + builder.headers, + builder.base64Encoded + ) + + companion object { + inline fun build(block: Builder.() -> Unit) = Builder().apply(block).build() + } + + class Builder { + + var statusCode: Int = 200 + var rawBody: String? = null + var headers: Map? = emptyMap() + var objectBody: Response? = null + var binaryBody: ByteArray? = null + var base64Encoded: Boolean = false + + fun build(): ApiGatewayResponse { + var body: String? = null + + if (rawBody != null) { + body = rawBody as String + } + else if (objectBody != null) { + // todo + } else if (binaryBody != null) { + // todo + } + + return ApiGatewayResponse(statusCode, body, headers, base64Encoded) + } + } +} diff --git a/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Handler.kt b/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Handler.kt new file mode 100644 index 000000000..d9b6ddf00 --- /dev/null +++ b/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Handler.kt @@ -0,0 +1,16 @@ +class Handler(input: Map, context: Any) { + init { + handleRequest(input, context) + } + + fun handleRequest(input: Map, context: Any): ApiGatewayResponse { + println("Received: " + input); + + val responseBody: Response = Response("Go Serverless v1.x! Your Kotlin function executed successfully!", input); + return ApiGatewayResponse.build { + statusCode = 200 + objectBody = responseBody + headers = hashMapOf("X-Powered-By" to "AWS Lambda & serverless") + } + } +} diff --git a/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Response.kt b/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Response.kt new file mode 100644 index 000000000..730bb8183 --- /dev/null +++ b/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Response.kt @@ -0,0 +1,6 @@ +class Response(message:String, input:Map) { + val message: String = message + get + val input: Map = input + get +} diff --git a/lib/plugins/create/templates/aws-kotlin-node/src/test/kotlin/gitkeep b/lib/plugins/create/templates/aws-kotlin-node/src/test/kotlin/gitkeep new file mode 100644 index 000000000..e69de29bb From 509096b49a5e4f266dd79495f2068fccf3c1e2fe Mon Sep 17 00:00:00 2001 From: Simon Males Date: Sat, 9 Sep 2017 23:30:09 +0200 Subject: [PATCH 17/56] Remove LAMBDA integration from ECMA sample --- .../create/templates/aws-nodejs-ecma-script/serverless.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/plugins/create/templates/aws-nodejs-ecma-script/serverless.yml b/lib/plugins/create/templates/aws-nodejs-ecma-script/serverless.yml index f732c5a9c..62e5090fb 100644 --- a/lib/plugins/create/templates/aws-nodejs-ecma-script/serverless.yml +++ b/lib/plugins/create/templates/aws-nodejs-ecma-script/serverless.yml @@ -24,4 +24,3 @@ functions: - http: method: get path: second - integration: lambda From c304aab55d1e1fa8d81f286b6a813bf083219275 Mon Sep 17 00:00:00 2001 From: Rafal Wilinski Date: Mon, 11 Sep 2017 18:34:12 +0200 Subject: [PATCH 18/56] Change javaBridgePath to include pom.xml --- lib/plugins/aws/invokeLocal/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugins/aws/invokeLocal/index.js b/lib/plugins/aws/invokeLocal/index.js index d1064a388..bf53e2113 100644 --- a/lib/plugins/aws/invokeLocal/index.js +++ b/lib/plugins/aws/invokeLocal/index.js @@ -210,7 +210,7 @@ class AwsInvokeLocal { const mvn = spawn('mvn', [ 'package', '-f', - javaBridgePath, + path.join(javaBridgePath, 'pom.xml'), ]); this.serverless.cli.consoleLog( From 2d09606cba226bf5e389fc55c570e122c094a6fc Mon Sep 17 00:00:00 2001 From: Rafal Wilinski Date: Mon, 11 Sep 2017 18:38:41 +0200 Subject: [PATCH 19/56] Move gitignore declaration --- .gitignore | 1 - lib/plugins/aws/invokeLocal/.gitignore | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 52fb14cbd..b49e4e927 100755 --- a/.gitignore +++ b/.gitignore @@ -48,4 +48,3 @@ tracking-config.json # Misc jest -lib/plugins/aws/invokeLocal/java/target diff --git a/lib/plugins/aws/invokeLocal/.gitignore b/lib/plugins/aws/invokeLocal/.gitignore index 0d20b6487..9b2db0a6c 100644 --- a/lib/plugins/aws/invokeLocal/.gitignore +++ b/lib/plugins/aws/invokeLocal/.gitignore @@ -1 +1,2 @@ *.pyc +java/target \ No newline at end of file From f3d9d0f53caac02e2dbb00594444208b584e0e08 Mon Sep 17 00:00:00 2001 From: Rafal Wilinski Date: Tue, 12 Sep 2017 14:39:18 +0200 Subject: [PATCH 20/56] Add toString() notice --- lib/plugins/aws/invokeLocal/index.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/plugins/aws/invokeLocal/index.js b/lib/plugins/aws/invokeLocal/index.js index bf53e2113..b43650255 100644 --- a/lib/plugins/aws/invokeLocal/index.js +++ b/lib/plugins/aws/invokeLocal/index.js @@ -179,6 +179,11 @@ class AwsInvokeLocal { path.join(__dirname, 'java', 'target', 'invoke-bridge-1.0.jar'), ]); + this.serverless.cli.consoleLog([ + 'In order to get human-readable output,', + 'please implement "toString()" method of your "ApiGatewayResponse" object.', + ].join(' ')); + java.stdout.on('data', (buf) => this.serverless.cli.consoleLog(buf.toString())); java.stderr.on('data', (buf) => this.serverless.cli.consoleLog(buf.toString())); java.stdin.write(input); From a989b4e7d28098f598cd62c4947f7ef6ffa3614e Mon Sep 17 00:00:00 2001 From: Rafal Wilinski Date: Tue, 12 Sep 2017 14:48:26 +0200 Subject: [PATCH 21/56] Get rid of log4j warnings --- lib/plugins/aws/invokeLocal/.gitignore | 5 ++++- .../aws-java-maven/src/main/java/com/serverless/Handler.java | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/plugins/aws/invokeLocal/.gitignore b/lib/plugins/aws/invokeLocal/.gitignore index 9b2db0a6c..376be9d3e 100644 --- a/lib/plugins/aws/invokeLocal/.gitignore +++ b/lib/plugins/aws/invokeLocal/.gitignore @@ -1,2 +1,5 @@ *.pyc -java/target \ No newline at end of file +java/target +java/.project +java/.settings +java/.classpath diff --git a/lib/plugins/create/templates/aws-java-maven/src/main/java/com/serverless/Handler.java b/lib/plugins/create/templates/aws-java-maven/src/main/java/com/serverless/Handler.java index 80a6f78e8..6d6c670e5 100644 --- a/lib/plugins/create/templates/aws-java-maven/src/main/java/com/serverless/Handler.java +++ b/lib/plugins/create/templates/aws-java-maven/src/main/java/com/serverless/Handler.java @@ -3,6 +3,7 @@ package com.serverless; import java.util.Collections; import java.util.Map; +import org.apache.log4j.BasicConfigurator; import org.apache.log4j.Logger; import com.amazonaws.services.lambda.runtime.Context; @@ -14,6 +15,8 @@ public class Handler implements RequestHandler, ApiGatewayRe @Override public ApiGatewayResponse handleRequest(Map input, Context context) { + BasicConfigurator.configure(); + LOG.info("received: " + input); Response responseBody = new Response("Go Serverless v1.x! Your function executed successfully!", input); return ApiGatewayResponse.builder() From 105de7a60f1573b6cc0c9510b4d455b6b9cb10de Mon Sep 17 00:00:00 2001 From: Rafal Wilinski Date: Tue, 12 Sep 2017 15:01:16 +0200 Subject: [PATCH 22/56] Throw an error if artifact doesn't exist --- lib/plugins/aws/invokeLocal/index.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/plugins/aws/invokeLocal/index.js b/lib/plugins/aws/invokeLocal/index.js index b43650255..1c2261c70 100644 --- a/lib/plugins/aws/invokeLocal/index.js +++ b/lib/plugins/aws/invokeLocal/index.js @@ -2,6 +2,7 @@ const BbPromise = require('bluebird'); const _ = require('lodash'); +const fs = require('fs'); const path = require('path'); const validate = require('../lib/validate'); const chalk = require('chalk'); @@ -172,6 +173,9 @@ class AwsInvokeLocal { callJavaBridge(artifactPath, className, input) { return new BbPromise((resolve) => { + // Throw an error if artifact doesn't exists + fs.statSync(artifactPath); + const java = spawn('java', [ `-DartifactPath=${artifactPath}`, `-DclassName=${className}`, From da782b85f4c2ad715f6f62cc54284657dc9e7edc Mon Sep 17 00:00:00 2001 From: Loren Gordon Date: Mon, 11 Sep 2017 16:30:34 -0400 Subject: [PATCH 23/56] Escapes the hyphen in the ssm variable syntax Fixes #4248 --- lib/classes/Variables.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/classes/Variables.js b/lib/classes/Variables.js index 5e49ac054..7e8c0a888 100644 --- a/lib/classes/Variables.js +++ b/lib/classes/Variables.js @@ -21,7 +21,7 @@ class Variables { this.cfRefSyntax = RegExp(/^cf:/g); this.s3RefSyntax = RegExp(/^s3:(.+?)\/(.+)$/); this.stringRefSynax = RegExp(/('.*')|(".*")/g); - this.ssmRefSyntax = RegExp(/^ssm:([a-zA-Z0-9_.-/]+)[~]?(true|false)?/); + this.ssmRefSyntax = RegExp(/^ssm:([a-zA-Z0-9_.\-/]+)[~]?(true|false)?/); } loadVariableSyntax() { From df47f9958e404e91857f3c76852e9add36e16127 Mon Sep 17 00:00:00 2001 From: Rafal Wilinski Date: Wed, 13 Sep 2017 09:29:26 +0200 Subject: [PATCH 24/56] Add @JsName annotation --- .../src/main/kotlin/com/serverless/Handler.kt | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Handler.kt b/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Handler.kt index d9b6ddf00..560878ba9 100644 --- a/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Handler.kt +++ b/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Handler.kt @@ -1,16 +1,11 @@ -class Handler(input: Map, context: Any) { - init { - handleRequest(input, context) - } +@JsName("Handler") +public fun Handler(input: Map, context: Any): ApiGatewayResponse { + println("Received: " + input); - fun handleRequest(input: Map, context: Any): ApiGatewayResponse { - println("Received: " + input); - - val responseBody: Response = Response("Go Serverless v1.x! Your Kotlin function executed successfully!", input); - return ApiGatewayResponse.build { - statusCode = 200 - objectBody = responseBody - headers = hashMapOf("X-Powered-By" to "AWS Lambda & serverless") - } + val responseBody: Response = Response("Go Serverless v1.x! Your Kotlin function executed successfully!", input); + return ApiGatewayResponse.build { + statusCode = 200 + objectBody = responseBody + headers = hashMapOf("X-Powered-By" to "AWS Lambda & serverless") } } From 1934c5f66a4d4d7854a395ba7a4c547e154645d1 Mon Sep 17 00:00:00 2001 From: Rafal Wilinski Date: Wed, 13 Sep 2017 10:14:30 +0200 Subject: [PATCH 25/56] Fix object to JSON conversions --- .../kotlin/com/serverless/ApiGatewayResponse.kt | 11 ++++++----- .../src/main/kotlin/com/serverless/Handler.kt | 13 ++++++++----- .../src/main/kotlin/com/serverless/Response.kt | 8 +++++++- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/ApiGatewayResponse.kt b/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/ApiGatewayResponse.kt index 6b50ddbc2..5a86594e6 100644 --- a/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/ApiGatewayResponse.kt +++ b/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/ApiGatewayResponse.kt @@ -1,7 +1,7 @@ class ApiGatewayResponse( val statusCode: Int = 200, var body: String? = null, - val headers: Map? = emptyMap(), + val headers: dynamic, val isBase64Encoded: Boolean = false ) { private constructor(builder: Builder) : this( @@ -15,11 +15,12 @@ class ApiGatewayResponse( inline fun build(block: Builder.() -> Unit) = Builder().apply(block).build() } - class Builder { + override fun toString(): String = "body" + class Builder { var statusCode: Int = 200 var rawBody: String? = null - var headers: Map? = emptyMap() + var headers: dynamic = object{} var objectBody: Response? = null var binaryBody: ByteArray? = null var base64Encoded: Boolean = false @@ -31,9 +32,9 @@ class ApiGatewayResponse( body = rawBody as String } else if (objectBody != null) { - // todo + body = objectBody.toString() } else if (binaryBody != null) { - // todo + body = "todo" } return ApiGatewayResponse(statusCode, body, headers, base64Encoded) diff --git a/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Handler.kt b/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Handler.kt index 560878ba9..22af54b6f 100644 --- a/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Handler.kt +++ b/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Handler.kt @@ -1,11 +1,14 @@ @JsName("Handler") -public fun Handler(input: Map, context: Any): ApiGatewayResponse { +public fun Handler(input: Map, context: Any, callback: (Any?, ApiGatewayResponse) -> ApiGatewayResponse): Any { println("Received: " + input); val responseBody: Response = Response("Go Serverless v1.x! Your Kotlin function executed successfully!", input); - return ApiGatewayResponse.build { - statusCode = 200 + val responseHeaders: dynamic = object{} + responseHeaders["X-Powered-By"] = "AWS Lambda & serverless" + + return callback(null, ApiGatewayResponse.build { + statusCode = 201 objectBody = responseBody - headers = hashMapOf("X-Powered-By" to "AWS Lambda & serverless") - } + headers = responseHeaders + }) } diff --git a/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Response.kt b/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Response.kt index 730bb8183..552ea5ba6 100644 --- a/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Response.kt +++ b/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Response.kt @@ -1,6 +1,12 @@ -class Response(message:String, input:Map) { +class Response(message: String, input: Map) { val message: String = message get val input: Map = input get + + override fun toString(): String { + val str: dynamic = object{} + str[message] = input; + return str; + } } From f0defb987a80576652bf6b04e0e2e84df1fc72c1 Mon Sep 17 00:00:00 2001 From: Rafal Wilinski Date: Wed, 13 Sep 2017 10:23:00 +0200 Subject: [PATCH 26/56] Add support for binary data --- .../src/main/kotlin/com/serverless/ApiGatewayResponse.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/ApiGatewayResponse.kt b/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/ApiGatewayResponse.kt index 5a86594e6..01b4146ee 100644 --- a/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/ApiGatewayResponse.kt +++ b/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/ApiGatewayResponse.kt @@ -34,7 +34,7 @@ class ApiGatewayResponse( else if (objectBody != null) { body = objectBody.toString() } else if (binaryBody != null) { - body = "todo" + body = binaryBody.toString() } return ApiGatewayResponse(statusCode, body, headers, base64Encoded) From 3d1dbd97bd725207ca3dad3d80fe20fe04a30cd6 Mon Sep 17 00:00:00 2001 From: Rafal Wilinski Date: Wed, 13 Sep 2017 10:32:26 +0200 Subject: [PATCH 27/56] Add template tests --- docker-compose.yml | 4 +++ docs/providers/aws/cli-reference/create.md | 1 + docs/providers/aws/guide/services.md | 1 + lib/plugins/create/create.js | 1 + lib/plugins/create/create.test.js | 27 +++++++++++++++++++ .../com/serverless/ApiGatewayResponse.kt | 11 +++----- tests/templates/test_all_templates | 1 + 7 files changed, 39 insertions(+), 7 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index d16161c14..fa90008cc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -39,6 +39,10 @@ services: image: maven:3-jdk-8 volumes: - ./tmp/serverless-integration-test-aws-kotlin-jvm-maven:/app + aws-kotlin-node: + image: node:6.10.3 + volumes: + - ./tmp/serverless-integration-test-aws-kotlin-node:/app aws-groovy-gradle: image: java:8 volumes: diff --git a/docs/providers/aws/cli-reference/create.md b/docs/providers/aws/cli-reference/create.md index 268ace7ed..38331142c 100644 --- a/docs/providers/aws/cli-reference/create.md +++ b/docs/providers/aws/cli-reference/create.md @@ -46,6 +46,7 @@ Most commonly used templates: - aws-python - aws-python3 - aws-kotlin-jvm-maven +- aws-kotlin-node - aws-groovy-gradle - aws-java-maven - aws-java-gradle diff --git a/docs/providers/aws/guide/services.md b/docs/providers/aws/guide/services.md index d6e18d93c..8d8575470 100644 --- a/docs/providers/aws/guide/services.md +++ b/docs/providers/aws/guide/services.md @@ -56,6 +56,7 @@ Here are the available runtimes for AWS Lambda: * aws-python * aws-python3 * aws-kotlin-jvm-maven +* aws-kotlin-node * aws-groovy-gradle * aws-java-gradle * aws-java-maven diff --git a/lib/plugins/create/create.js b/lib/plugins/create/create.js index d552a1732..3cf8067f7 100644 --- a/lib/plugins/create/create.js +++ b/lib/plugins/create/create.js @@ -17,6 +17,7 @@ const validTemplates = [ 'aws-java-maven', 'aws-java-gradle', 'aws-kotlin-jvm-maven', + 'aws-kotlin-node', 'aws-scala-sbt', 'aws-csharp', 'aws-fsharp', diff --git a/lib/plugins/create/create.test.js b/lib/plugins/create/create.test.js index cf3591dc1..9d8e9ad2d 100644 --- a/lib/plugins/create/create.test.js +++ b/lib/plugins/create/create.test.js @@ -228,6 +228,33 @@ describe('Create', () => { }); }); + it('should generate scaffolding for "aws-kotlin-node" template', () => { + process.chdir(tmpDir); + create.options.template = 'aws-kotlin-node'; + + return create.create().then(() => { + const dirContent = walkDirSync(tmpDir) + .map(elem => elem.replace(path.join(tmpDir, path.sep), '')); + + expect(dirContent).to.include('serverless.yml'); + expect(dirContent).to.include('build.gradle'); + expect(dirContent).to.include('gradlew'); + expect(dirContent).to.include('gradlew.bat'); + expect(dirContent).to.include(path.join('gradle', 'wrapper', + 'gradle-wrapper.jar')); + expect(dirContent).to.include(path.join('gradle', 'wrapper', + 'gradle-wrapper.properties')); + expect(dirContent).to.include(path.join('src', 'main', 'kotlin', 'com', 'serverless', + 'Handler.kt')); + expect(dirContent).to.include(path.join('src', 'main', 'kotlin', 'com', 'serverless', + 'ApiGatewayResponse.kt')); + expect(dirContent).to.include(path.join('src', 'main', 'kotlin', 'com', 'serverless', + 'Response.kt')); + expect(dirContent).to.include(path.join('src', 'test', 'kotlin', '.gitkeep')); + expect(dirContent).to.include(path.join('.gitignore')); + }); + }); + it('should generate scaffolding for "aws-java-gradle" template', () => { process.chdir(tmpDir); create.options.template = 'aws-java-gradle'; diff --git a/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/ApiGatewayResponse.kt b/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/ApiGatewayResponse.kt index 01b4146ee..de26017b7 100644 --- a/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/ApiGatewayResponse.kt +++ b/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/ApiGatewayResponse.kt @@ -28,13 +28,10 @@ class ApiGatewayResponse( fun build(): ApiGatewayResponse { var body: String? = null - if (rawBody != null) { - body = rawBody as String - } - else if (objectBody != null) { - body = objectBody.toString() - } else if (binaryBody != null) { - body = binaryBody.toString() + when { + rawBody != null -> body = rawBody as String + objectBody != null -> body = objectBody.toString() + binaryBody != null -> body = binaryBody.toString() } return ApiGatewayResponse(statusCode, body, headers, base64Encoded) diff --git a/tests/templates/test_all_templates b/tests/templates/test_all_templates index 2a78e6c9f..6ad858980 100755 --- a/tests/templates/test_all_templates +++ b/tests/templates/test_all_templates @@ -18,6 +18,7 @@ integration-test aws-nodejs integration-test aws-python integration-test aws-python3 integration-test aws-kotlin-jvm-maven +integration-test aws-kotlin-node integration-test aws-nodejs-typescript integration-test aws-nodejs-ecma-script integration-test google-nodejs From 665e5db55765765adf0f9868c28f11ae3fdd42ff Mon Sep 17 00:00:00 2001 From: Rafal Wilinski Date: Wed, 13 Sep 2017 10:38:23 +0200 Subject: [PATCH 28/56] Fix .gitkeep file name --- .../aws-kotlin-node/src/test/kotlin/{gitkeep => .gitkeep} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lib/plugins/create/templates/aws-kotlin-node/src/test/kotlin/{gitkeep => .gitkeep} (100%) diff --git a/lib/plugins/create/templates/aws-kotlin-node/src/test/kotlin/gitkeep b/lib/plugins/create/templates/aws-kotlin-node/src/test/kotlin/.gitkeep similarity index 100% rename from lib/plugins/create/templates/aws-kotlin-node/src/test/kotlin/gitkeep rename to lib/plugins/create/templates/aws-kotlin-node/src/test/kotlin/.gitkeep From 5e8d67ce4bb7df0e787a5e276b563aeacaacc00c Mon Sep 17 00:00:00 2001 From: Rafal Wilinski Date: Wed, 13 Sep 2017 10:59:14 +0200 Subject: [PATCH 29/56] Code review fixes --- lib/plugins/aws/invokeLocal/index.js | 2 +- lib/plugins/aws/invokeLocal/index.test.js | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/plugins/aws/invokeLocal/index.js b/lib/plugins/aws/invokeLocal/index.js index 1c2261c70..0c43230bd 100644 --- a/lib/plugins/aws/invokeLocal/index.js +++ b/lib/plugins/aws/invokeLocal/index.js @@ -183,7 +183,7 @@ class AwsInvokeLocal { path.join(__dirname, 'java', 'target', 'invoke-bridge-1.0.jar'), ]); - this.serverless.cli.consoleLog([ + this.serverless.cli.log([ 'In order to get human-readable output,', 'please implement "toString()" method of your "ApiGatewayResponse" object.', ].join(' ')); diff --git a/lib/plugins/aws/invokeLocal/index.test.js b/lib/plugins/aws/invokeLocal/index.test.js index 796cfd630..79655d1e5 100644 --- a/lib/plugins/aws/invokeLocal/index.test.js +++ b/lib/plugins/aws/invokeLocal/index.test.js @@ -7,6 +7,7 @@ const BbPromise = require('bluebird'); const mockRequire = require('mock-require'); const EventEmitter = require('events'); const fs = BbPromise.promisifyAll(require('graceful-fs')); +const fsExtra = require('fs-extra'); const AwsInvokeLocal = require('./index'); const AwsProvider = require('../provider/awsProvider'); const Serverless = require('../../../Serverless'); @@ -379,7 +380,7 @@ describe('AwsInvokeLocal', () => { }); it('throw error when using runtime other than Node.js or Python', () => { - awsInvokeLocal.options.functionObj.runtime = 'go'; + awsInvokeLocal.options.functionObj.runtime = 'invalid-runtime'; expect(() => awsInvokeLocal.invokeLocal()).to.throw(Error); delete awsInvokeLocal.options.functionObj.runtime; }); @@ -591,6 +592,7 @@ describe('AwsInvokeLocal', () => { }); describe('#invokeLocalJava', () => { + const bridgePath = path.join(__dirname, 'java', 'target'); let callJavaBridgeStub; beforeEach(() => { @@ -612,12 +614,12 @@ describe('AwsInvokeLocal', () => { afterEach(() => { serverless.cli.consoleLog.restore(); - callJavaBridgeStub.restore(); + awsInvokeLocal.callJavaBridge.restore(); + fsExtra.removeSync(bridgePath); }); it('should invoke callJavaBridge when bridge is built', () => { - const bridgePath = path.join(__dirname, 'java', 'target'); - fs.mkdir(bridgePath); + fs.mkdirSync(bridgePath); awsInvokeLocal.invokeLocalJava( 'java', @@ -639,7 +641,6 @@ describe('AwsInvokeLocal', () => { }, }) )).to.be.equal(true); - fs.rmdirSync(bridgePath); }); }); @@ -683,7 +684,7 @@ describe('AwsInvokeLocal', () => { }); afterEach(() => { - callJavaBridgeMockedStub.restore(); + awsInvokeLocalMocked.callJavaBridge.restore(); delete require.cache[require.resolve('./index')]; delete require.cache[require.resolve('child_process')]; }); From 6b74a9c68bc9f710a407eef70485d94e919723d5 Mon Sep 17 00:00:00 2001 From: Rafal Wilinski Date: Wed, 13 Sep 2017 11:09:45 +0200 Subject: [PATCH 30/56] Change fs sync calls to async graceful --- lib/plugins/aws/invokeLocal/index.js | 49 ++++++++++++++-------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/lib/plugins/aws/invokeLocal/index.js b/lib/plugins/aws/invokeLocal/index.js index 0c43230bd..4996f6402 100644 --- a/lib/plugins/aws/invokeLocal/index.js +++ b/lib/plugins/aws/invokeLocal/index.js @@ -2,7 +2,7 @@ const BbPromise = require('bluebird'); const _ = require('lodash'); -const fs = require('fs'); +const fs = BbPromise.promisifyAll(require('graceful-fs')); const path = require('path'); const validate = require('../lib/validate'); const chalk = require('chalk'); @@ -173,26 +173,27 @@ class AwsInvokeLocal { callJavaBridge(artifactPath, className, input) { return new BbPromise((resolve) => { - // Throw an error if artifact doesn't exists - fs.statSync(artifactPath); + fs.statAsync(artifactPath).then(() => { + const java = spawn('java', [ + `-DartifactPath=${artifactPath}`, + `-DclassName=${className}`, + '-jar', + path.join(__dirname, 'java', 'target', 'invoke-bridge-1.0.jar'), + ]); - const java = spawn('java', [ - `-DartifactPath=${artifactPath}`, - `-DclassName=${className}`, - '-jar', - path.join(__dirname, 'java', 'target', 'invoke-bridge-1.0.jar'), - ]); + this.serverless.cli.log([ + 'In order to get human-readable output,', + 'please implement "toString()" method of your "ApiGatewayResponse" object.', + ].join(' ')); - this.serverless.cli.log([ - 'In order to get human-readable output,', - 'please implement "toString()" method of your "ApiGatewayResponse" object.', - ].join(' ')); - - java.stdout.on('data', (buf) => this.serverless.cli.consoleLog(buf.toString())); - java.stderr.on('data', (buf) => this.serverless.cli.consoleLog(buf.toString())); - java.stdin.write(input); - java.stdin.end(); - java.on('close', () => resolve()); + java.stdout.on('data', (buf) => this.serverless.cli.consoleLog(buf.toString())); + java.stderr.on('data', (buf) => this.serverless.cli.consoleLog(buf.toString())); + java.stdin.write(input); + java.stdin.end(); + java.on('close', () => resolve()); + }).catch(() => { + throw new Error(`Artifact ${artifactPath} doesn't exists, please compile it first.`); + }); }); } @@ -215,14 +216,16 @@ class AwsInvokeLocal { const executablePath = path.join(javaBridgePath, 'target'); return new BbPromise(resolve => { - if (!this.serverless.utils.dirExistsSync(executablePath)) { + fs.statAsync(executablePath).then(() => { + this.callJavaBridge(artifactPath, className, input).then(resolve); + }).catch(() => { const mvn = spawn('mvn', [ 'package', '-f', path.join(javaBridgePath, 'pom.xml'), ]); - this.serverless.cli.consoleLog( + this.serverless.cli.log( 'Building Java bridge, first invocation might take a bit longer.' ); @@ -232,9 +235,7 @@ class AwsInvokeLocal { mvn.on('close', () => { this.callJavaBridge(artifactPath, className, input).then(resolve); }); - } else { - this.callJavaBridge(artifactPath, className, input).then(resolve); - } + }); }); } From 5951b64d73f8458289780548e86d635b33ed96be Mon Sep 17 00:00:00 2001 From: Rafal Wilinski Date: Wed, 13 Sep 2017 11:46:31 +0200 Subject: [PATCH 31/56] Fix passed JS object types --- docker-compose.yml | 2 +- .../create/templates/aws-kotlin-node/serverless.yml | 4 ++-- .../main/kotlin/com/serverless/ApiGatewayResponse.kt | 9 --------- .../src/main/kotlin/com/serverless/Handler.kt | 4 ++-- .../src/main/kotlin/com/serverless/Response.kt | 10 +++++----- 5 files changed, 10 insertions(+), 19 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index fa90008cc..d674d4a94 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -40,7 +40,7 @@ services: volumes: - ./tmp/serverless-integration-test-aws-kotlin-jvm-maven:/app aws-kotlin-node: - image: node:6.10.3 + image: pgoudreau/docker-maven-node volumes: - ./tmp/serverless-integration-test-aws-kotlin-node:/app aws-groovy-gradle: diff --git a/lib/plugins/create/templates/aws-kotlin-node/serverless.yml b/lib/plugins/create/templates/aws-kotlin-node/serverless.yml index 2a453d411..a2a89013c 100644 --- a/lib/plugins/create/templates/aws-kotlin-node/serverless.yml +++ b/lib/plugins/create/templates/aws-kotlin-node/serverless.yml @@ -19,7 +19,7 @@ service: aws-kotlin-node # NOTE: update this with your service name provider: name: aws - runtime: node6.10 + runtime: nodejs6.10 # you can overwrite defaults here # stage: dev @@ -51,7 +51,7 @@ package: functions: hello: - handler: Handler + handler: build/index.Handler # The following are a few example events you can configure # NOTE: Please make sure to change your handler code to work with those events diff --git a/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/ApiGatewayResponse.kt b/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/ApiGatewayResponse.kt index de26017b7..ce3a5f365 100644 --- a/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/ApiGatewayResponse.kt +++ b/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/ApiGatewayResponse.kt @@ -4,19 +4,10 @@ class ApiGatewayResponse( val headers: dynamic, val isBase64Encoded: Boolean = false ) { - private constructor(builder: Builder) : this( - builder.statusCode, - builder.rawBody, - builder.headers, - builder.base64Encoded - ) - companion object { inline fun build(block: Builder.() -> Unit) = Builder().apply(block).build() } - override fun toString(): String = "body" - class Builder { var statusCode: Int = 200 var rawBody: String? = null diff --git a/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Handler.kt b/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Handler.kt index 22af54b6f..bdd4344dc 100644 --- a/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Handler.kt +++ b/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Handler.kt @@ -1,6 +1,6 @@ @JsName("Handler") -public fun Handler(input: Map, context: Any, callback: (Any?, ApiGatewayResponse) -> ApiGatewayResponse): Any { - println("Received: " + input); +public fun Handler(input: dynamic = {}, context: Any, callback: (Any?, ApiGatewayResponse) -> ApiGatewayResponse): Any { + println("Received: " + js("JSON.stringify(input)")); val responseBody: Response = Response("Go Serverless v1.x! Your Kotlin function executed successfully!", input); val responseHeaders: dynamic = object{} diff --git a/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Response.kt b/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Response.kt index 552ea5ba6..a4809294c 100644 --- a/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Response.kt +++ b/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Response.kt @@ -1,12 +1,12 @@ -class Response(message: String, input: Map) { +class Response(message: String, input: dynamic) { val message: String = message get - val input: Map = input + val input: dynamic = input get override fun toString(): String { - val str: dynamic = object{} - str[message] = input; - return str; + val stringified = js("JSON.stringify(this.input)") + + return "{\"$message\": ${stringified} }"; } } From dc7b78ccf3298dcd548bb067eb2d70f62f13c6b2 Mon Sep 17 00:00:00 2001 From: Rafal Wilinski Date: Wed, 13 Sep 2017 11:51:10 +0200 Subject: [PATCH 32/56] Remove artifact package --- lib/plugins/create/templates/aws-kotlin-node/serverless.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/plugins/create/templates/aws-kotlin-node/serverless.yml b/lib/plugins/create/templates/aws-kotlin-node/serverless.yml index a2a89013c..4cb7f89d2 100644 --- a/lib/plugins/create/templates/aws-kotlin-node/serverless.yml +++ b/lib/plugins/create/templates/aws-kotlin-node/serverless.yml @@ -45,10 +45,6 @@ provider: # environment: # variable1: value1 -# you can add packaging information here -package: - artifact: build/index.js - functions: hello: handler: build/index.Handler From 629c073c6b94148f9babaa9f2d34e1e0267af73b Mon Sep 17 00:00:00 2001 From: Loren Gordon Date: Wed, 13 Sep 2017 07:14:49 -0400 Subject: [PATCH 33/56] Tests SSM variable syntax for all allowable characters --- lib/classes/Variables.test.js | 73 ++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/lib/classes/Variables.test.js b/lib/classes/Variables.test.js index 3efcd7eb6..ac281e9f7 100644 --- a/lib/classes/Variables.test.js +++ b/lib/classes/Variables.test.js @@ -946,21 +946,23 @@ describe('Variables', () => { }); it('should get variable from Ssm using regular-style param', () => { + const param = 'Param-01_valid.chars'; + const value = 'MockValue'; const awsResponseMock = { Parameter: { - Value: 'MockValue', + Value: value, }, }; const ssmStub = sinon.stub(awsProvider, 'request').resolves(awsResponseMock); - return serverless.variables.getValueFromSsm('ssm:param').then(value => { - expect(value).to.be.equal('MockValue'); + return serverless.variables.getValueFromSsm(`ssm:${param}`).then(resolved => { + expect(resolved).to.be.equal(value); expect(ssmStub.calledOnce).to.be.equal(true); expect(ssmStub.calledWithExactly( 'SSM', 'getParameter', { - Name: 'param', + Name: param, WithDecryption: false, }, serverless.variables.options.stage, @@ -970,21 +972,23 @@ describe('Variables', () => { }); it('should get variable from Ssm using path-style param', () => { + const param = '/path/to/Param-01_valid.chars'; + const value = 'MockValue'; const awsResponseMock = { Parameter: { - Value: 'MockValue', + Value: value, }, }; const ssmStub = sinon.stub(awsProvider, 'request').resolves(awsResponseMock); - return serverless.variables.getValueFromSsm('ssm:/some/path/to/param').then(value => { - expect(value).to.be.equal('MockValue'); + return serverless.variables.getValueFromSsm(`ssm:${param}`).then(resolved => { + expect(resolved).to.be.equal(value); expect(ssmStub.calledOnce).to.be.equal(true); expect(ssmStub.calledWithExactly( 'SSM', 'getParameter', { - Name: '/some/path/to/param', + Name: param, WithDecryption: false, }, serverless.variables.options.stage, @@ -994,21 +998,23 @@ describe('Variables', () => { }); it('should get encrypted variable from Ssm using extended syntax', () => { + const param = '/path/to/Param-01_valid.chars'; + const value = 'MockValue'; const awsResponseMock = { Parameter: { - Value: 'MockValue', + Value: value, }, }; const ssmStub = sinon.stub(awsProvider, 'request').resolves(awsResponseMock); - return serverless.variables.getValueFromSsm('ssm:/some/path/to/param~true').then(value => { - expect(value).to.be.equal('MockValue'); + return serverless.variables.getValueFromSsm(`ssm:${param}~true`).then(resolved => { + expect(resolved).to.be.equal(value); expect(ssmStub.calledOnce).to.be.equal(true); expect(ssmStub.calledWithExactly( 'SSM', 'getParameter', { - Name: '/some/path/to/param', + Name: param, WithDecryption: true, }, serverless.variables.options.stage, @@ -1018,21 +1024,23 @@ describe('Variables', () => { }); it('should get unencrypted variable from Ssm using extended syntax', () => { + const param = '/path/to/Param-01_valid.chars'; + const value = 'MockValue'; const awsResponseMock = { Parameter: { - Value: 'MockValue', + Value: value, }, }; const ssmStub = sinon.stub(awsProvider, 'request').resolves(awsResponseMock); - return serverless.variables.getValueFromSsm('ssm:/some/path/to/param~false').then(value => { - expect(value).to.be.equal('MockValue'); + return serverless.variables.getValueFromSsm(`ssm:${param}~false`).then(resolved => { + expect(resolved).to.be.equal(value); expect(ssmStub.calledOnce).to.be.equal(true); expect(ssmStub.calledWithExactly( 'SSM', 'getParameter', { - Name: '/some/path/to/param', + Name: param, WithDecryption: false, }, serverless.variables.options.stage, @@ -1042,28 +1050,29 @@ describe('Variables', () => { }); it('should ignore bad values for extended syntax', () => { + const param = '/path/to/Param-01_valid.chars'; + const value = 'MockValue'; const awsResponseMock = { Parameter: { - Value: 'MockValue', + Value: value, }, }; const ssmStub = sinon.stub(awsProvider, 'request').resolves(awsResponseMock); - return serverless.variables.getValueFromSsm('ssm:/some/path/to/param~badvalue') - .then(value => { - expect(value).to.be.equal('MockValue'); - expect(ssmStub.calledOnce).to.be.equal(true); - expect(ssmStub.calledWithExactly( - 'SSM', - 'getParameter', - { - Name: '/some/path/to/param', - WithDecryption: false, - }, - serverless.variables.options.stage, - serverless.variables.options.region - )).to.be.equal(true); - }); + return serverless.variables.getValueFromSsm(`ssm:${param}~badvalue`).then(resolved => { + expect(resolved).to.be.equal(value); + expect(ssmStub.calledOnce).to.be.equal(true); + expect(ssmStub.calledWithExactly( + 'SSM', + 'getParameter', + { + Name: param, + WithDecryption: false, + }, + serverless.variables.options.stage, + serverless.variables.options.region + )).to.be.equal(true); + }); }); it('should return undefined if SSM parameter does not exist', () => { From bc86be22522c42dcc2c1ba20081aac15fd50e13a Mon Sep 17 00:00:00 2001 From: Rafal Wilinski Date: Wed, 13 Sep 2017 16:48:23 +0200 Subject: [PATCH 34/56] Add --no-color flag notice --- lib/classes/CLI.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/classes/CLI.js b/lib/classes/CLI.js index 4a186a1a5..66d4517b3 100644 --- a/lib/classes/CLI.js +++ b/lib/classes/CLI.js @@ -140,6 +140,7 @@ class CLI { this.consoleLog(chalk.yellow.underline('Commands')); this.consoleLog(chalk.dim('* You can run commands with "serverless" or the shortcut "sls"')); this.consoleLog(chalk.dim('* Pass "--verbose" to this command to get in-depth plugin info')); + this.consoleLog(chalk.dim('* Pass "--no-color" to get disable CLI colors')); this.consoleLog(chalk.dim('* Pass "--help" after any for contextual help')); this.consoleLog(''); From 6d893aee903baffc62636287a6850ef31863685b Mon Sep 17 00:00:00 2001 From: Rafal Wilinski Date: Wed, 13 Sep 2017 16:53:04 +0200 Subject: [PATCH 35/56] Fix typo --- lib/classes/CLI.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/classes/CLI.js b/lib/classes/CLI.js index 66d4517b3..cb177428b 100644 --- a/lib/classes/CLI.js +++ b/lib/classes/CLI.js @@ -140,7 +140,7 @@ class CLI { this.consoleLog(chalk.yellow.underline('Commands')); this.consoleLog(chalk.dim('* You can run commands with "serverless" or the shortcut "sls"')); this.consoleLog(chalk.dim('* Pass "--verbose" to this command to get in-depth plugin info')); - this.consoleLog(chalk.dim('* Pass "--no-color" to get disable CLI colors')); + this.consoleLog(chalk.dim('* Pass "--no-color" to disable CLI colors')); this.consoleLog(chalk.dim('* Pass "--help" after any for contextual help')); this.consoleLog(''); From 0d7182550457f5bf965e2940af2b3f66948d8c07 Mon Sep 17 00:00:00 2001 From: Brendan Abbott Date: Thu, 14 Sep 2017 12:02:26 +1000 Subject: [PATCH 36/56] Remove outdated sentence about local runtime The Limitations section covers this, and is more accurate (includes Python as a possibility). --- docs/providers/aws/cli-reference/invoke.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/providers/aws/cli-reference/invoke.md b/docs/providers/aws/cli-reference/invoke.md index db3f2d2cc..515df23af 100644 --- a/docs/providers/aws/cli-reference/invoke.md +++ b/docs/providers/aws/cli-reference/invoke.md @@ -35,7 +35,7 @@ serverless invoke [local] --function functionName # Invoke Local -Invokes a function locally for testing and logs the output. You can only invoke Node.js runtime locally at the moment. Keep in mind that we mock the `context` with simple mock data. +Invokes a function locally for testing and logs the output. Keep in mind that we mock the `context` with simple mock data. ```bash serverless invoke local --function functionName From 89ea4664f0ba7a1ddada86d88fe5b9b73ab88c8b Mon Sep 17 00:00:00 2001 From: Ryan Lewis Date: Thu, 14 Sep 2017 00:53:36 -0700 Subject: [PATCH 37/56] updating docs --- .../providers/aws/cli-reference/deploy-function.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/providers/aws/cli-reference/deploy-function.md b/docs/providers/aws/cli-reference/deploy-function.md index f7bee94af..1b4486288 100644 --- a/docs/providers/aws/cli-reference/deploy-function.md +++ b/docs/providers/aws/cli-reference/deploy-function.md @@ -18,16 +18,16 @@ The `sls deploy function` command deploys an individual function without AWS Clo serverless deploy function -f functionName ``` -**Note:** Because this command is only deploying the function code, function -properties such as environment variables and events will **not** be deployed. -Those properties are deployed via CloudFormation, which does not execute with -this command. +**Note:** This command **now** deploys both function configuration and code by +default. Just as before, this puts your function in an inconsistent state that +is out of sync with your CloudFormation stack. Use this for faster development +cycles and not production deployments ## Options - `--function` or `-f` The name of the function which should be deployed - `--stage` or `-s` The stage in your service that you want to deploy to. - `--region` or `-r` The region in that stage that you want to deploy to. -- `--update-config` or `-u` Pushes Lambda-level configuration changes e.g. timeout or memorySize +- `--update-config` or `-u` Pushes ONLY Lambda-level configuration changes e.g. timeout or memorySize ## Examples @@ -43,8 +43,8 @@ serverless deploy function --function helloWorld serverless deploy function --function helloWorld --stage dev --region us-east-1 ``` -### Deployment with configuration change +### Deploy only configuration changes ```bash serverless deploy function --function helloWorld --update-config -``` \ No newline at end of file +``` From 43a2ce2822a34544bc9f19f25431aa44cf6330b4 Mon Sep 17 00:00:00 2001 From: Philipp Muens Date: Thu, 14 Sep 2017 12:17:00 +0200 Subject: [PATCH 38/56] Update Lambda function response --- .../templates/aws-nodejs-typescript/handler.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/plugins/create/templates/aws-nodejs-typescript/handler.ts b/lib/plugins/create/templates/aws-nodejs-typescript/handler.ts index 280d14b94..679db546b 100644 --- a/lib/plugins/create/templates/aws-nodejs-typescript/handler.ts +++ b/lib/plugins/create/templates/aws-nodejs-typescript/handler.ts @@ -1,3 +1,11 @@ -export const hello = (event, context, cb) => cb(null, - { message: 'Go Serverless Webpack (Typescript) v1.0! Your function executed successfully!', event } -); \ No newline at end of file +export const hello = (event, context, cb) => { + const response = { + statusCode: 200, + body: JSON.stringify({ + message: 'Go Serverless Webpack (Typescript) v1.0! Your function executed successfully!', + input: event, + }), + }; + + cb(null, response); +} From 8e48a3cd8e7b39bd9717009382c0d15fbfcd7799 Mon Sep 17 00:00:00 2001 From: Philipp Muens Date: Thu, 14 Sep 2017 12:31:43 +0200 Subject: [PATCH 39/56] Update other S3 calls to use upload as well --- lib/plugins/aws/deploy/lib/uploadArtifacts.js | 2 +- .../aws/deploy/lib/uploadArtifacts.test.js | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/plugins/aws/deploy/lib/uploadArtifacts.js b/lib/plugins/aws/deploy/lib/uploadArtifacts.js index 771e7eb01..a6b21e770 100644 --- a/lib/plugins/aws/deploy/lib/uploadArtifacts.js +++ b/lib/plugins/aws/deploy/lib/uploadArtifacts.js @@ -44,7 +44,7 @@ module.exports = { } return this.provider.request('S3', - 'putObject', + 'upload', params, this.options.stage, this.options.region); diff --git a/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js b/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js index 2e0d5ea3f..7af45d186 100644 --- a/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js +++ b/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js @@ -70,13 +70,13 @@ describe('uploadArtifacts', () => { describe('#uploadCloudFormationFile()', () => { let normalizeCloudFormationTemplateStub; - let putObjectStub; + let uploadStub; beforeEach(() => { normalizeCloudFormationTemplateStub = sinon .stub(normalizeFiles, 'normalizeCloudFormationTemplate') .returns(); - putObjectStub = sinon + uploadStub = sinon .stub(awsDeploy.provider, 'request') .resolves(); }); @@ -91,10 +91,10 @@ describe('uploadArtifacts', () => { return awsDeploy.uploadCloudFormationFile().then(() => { expect(normalizeCloudFormationTemplateStub.calledOnce).to.equal(true); - expect(putObjectStub.calledOnce).to.equal(true); - expect(putObjectStub.calledWithExactly( + expect(uploadStub.calledOnce).to.equal(true); + expect(uploadStub.calledWithExactly( 'S3', - 'putObject', + 'upload', { Bucket: awsDeploy.bucketName, Key: `${awsDeploy.serverless.service.package @@ -121,10 +121,10 @@ describe('uploadArtifacts', () => { return awsDeploy.uploadCloudFormationFile().then(() => { expect(normalizeCloudFormationTemplateStub.calledOnce).to.equal(true); - expect(putObjectStub.calledOnce).to.be.equal(true); - expect(putObjectStub.calledWithExactly( + expect(uploadStub.calledOnce).to.be.equal(true); + expect(uploadStub.calledWithExactly( 'S3', - 'putObject', + 'upload', { Bucket: awsDeploy.bucketName, Key: `${awsDeploy.serverless.service.package From e2eef3045382d2c13e4fa01d5da1d27dc9fb3314 Mon Sep 17 00:00:00 2001 From: Ryan Lewis Date: Thu, 14 Sep 2017 22:38:05 -0700 Subject: [PATCH 40/56] warn if bad value --- lib/utils/yamlAstParser.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/utils/yamlAstParser.js b/lib/utils/yamlAstParser.js index ff6d4435e..a86c7d4dd 100644 --- a/lib/utils/yamlAstParser.js +++ b/lib/utils/yamlAstParser.js @@ -5,6 +5,7 @@ const BbPromise = require('bluebird'); const fs = BbPromise.promisifyAll(require('fs')); const _ = require('lodash'); const os = require('os'); +const chalk = require('chalk'); const findKeyChain = (astContent) => { let content = astContent; @@ -24,6 +25,11 @@ const parseAST = (ymlAstContent, astObject) => { let newAstObject = astObject || {}; if (ymlAstContent.mappings && _.isArray(ymlAstContent.mappings)) { _.forEach(ymlAstContent.mappings, (v) => { + if (!v.value) { + console.log(`Serverless: ${chalk.red(`Your serverless.yml has an invalid value with key: ${v.key.value}. Ignoring...`)}`); + return; + } + if (v.key.kind === 0 && v.value.kind === 0) { newAstObject[findKeyChain(v)] = v.value; } else if (v.key.kind === 0 && v.value.kind === 2) { From 4f49a417f1a66fb0703c8e20521698c2fe208ec6 Mon Sep 17 00:00:00 2001 From: Ryan Lewis Date: Thu, 14 Sep 2017 22:41:16 -0700 Subject: [PATCH 41/56] adding test --- lib/utils/yamlAstParser.test.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/utils/yamlAstParser.test.js b/lib/utils/yamlAstParser.test.js index 0d3cfa332..9e04b4a0e 100644 --- a/lib/utils/yamlAstParser.test.js +++ b/lib/utils/yamlAstParser.test.js @@ -20,7 +20,7 @@ describe('#yamlAstParser', () => { it('should add a top level object and item into the yaml file', () => { const yamlFilePath = path.join(tmpDirPath, 'test.yaml'); - writeFileSync(yamlFilePath, 'serveice: test-service'); + writeFileSync(yamlFilePath, 'service: test-service'); return expect(yamlAstParser.addNewArrayItem(yamlFilePath, 'toplevel', 'foo')) .to.be.fulfilled.then(() => { const yaml = readFileSync(yamlFilePath, 'utf8'); @@ -44,7 +44,7 @@ describe('#yamlAstParser', () => { it('should add a multiple level object and item into the yaml file', () => { const yamlFilePath = path.join(tmpDirPath, 'test.yaml'); - writeFileSync(yamlFilePath, 'serveice: test-service'); + writeFileSync(yamlFilePath, 'service: test-service'); return expect(yamlAstParser.addNewArrayItem(yamlFilePath, 'toplevel.second.third', 'foo')) .to.be.fulfilled.then(() => { const yaml = readFileSync(yamlFilePath, 'utf8'); @@ -90,6 +90,18 @@ describe('#yamlAstParser', () => { expect(yaml.toplevel).to.be.deep.equal(['foo']); }); }); + + it('should survive with invalid yaml', () => { + const yamlFilePath = path.join(tmpDirPath, 'test.yaml'); + + writeFileSync(yamlFilePath, 'service:'); + return expect(yamlAstParser.addNewArrayItem(yamlFilePath, 'toplevel', 'foo')) + .to.be.fulfilled.then(() => { + const yaml = readFileSync(yamlFilePath, 'utf8'); + expect(yaml).to.have.property('toplevel'); + expect(yaml.toplevel).to.be.deep.equal(['foo']); + }); + }); }); describe('#removeExistingArrayItem()', () => { From 3a27890bb3a41c0ae132e015bcc15ba4539594c9 Mon Sep 17 00:00:00 2001 From: Rafal Wilinski Date: Fri, 15 Sep 2017 10:53:12 +0200 Subject: [PATCH 42/56] Rename template to `aws-kotlin-nodejs-gradle` --- docker-compose.yml | 4 ++-- docs/providers/aws/cli-reference/create.md | 2 +- docs/providers/aws/guide/services.md | 2 +- lib/plugins/create/create.js | 2 +- lib/plugins/create/create.test.js | 5 +++-- .../build.gradle | 0 .../gitignore | 0 .../gradle/wrapper/gradle-wrapper.jar | Bin .../gradle/wrapper/gradle-wrapper.properties | 0 .../gradlew | 0 .../gradlew.bat | 0 .../package.json | 0 .../serverless.yml | 2 +- .../kotlin/com/serverless/ApiGatewayResponse.kt | 0 .../src/main/kotlin/com/serverless/Handler.kt | 0 .../src/main/kotlin/com/serverless/Response.kt | 0 .../src/test/kotlin/.gitkeep | 0 tests/templates/test_all_templates | 2 +- 18 files changed, 10 insertions(+), 9 deletions(-) rename lib/plugins/create/templates/{aws-kotlin-node => aws-kotlin-nodejs-gradle}/build.gradle (100%) rename lib/plugins/create/templates/{aws-kotlin-node => aws-kotlin-nodejs-gradle}/gitignore (100%) rename lib/plugins/create/templates/{aws-kotlin-node => aws-kotlin-nodejs-gradle}/gradle/wrapper/gradle-wrapper.jar (100%) rename lib/plugins/create/templates/{aws-kotlin-node => aws-kotlin-nodejs-gradle}/gradle/wrapper/gradle-wrapper.properties (100%) rename lib/plugins/create/templates/{aws-kotlin-node => aws-kotlin-nodejs-gradle}/gradlew (100%) rename lib/plugins/create/templates/{aws-kotlin-node => aws-kotlin-nodejs-gradle}/gradlew.bat (100%) rename lib/plugins/create/templates/{aws-kotlin-node => aws-kotlin-nodejs-gradle}/package.json (100%) rename lib/plugins/create/templates/{aws-kotlin-node => aws-kotlin-nodejs-gradle}/serverless.yml (97%) rename lib/plugins/create/templates/{aws-kotlin-node => aws-kotlin-nodejs-gradle}/src/main/kotlin/com/serverless/ApiGatewayResponse.kt (100%) rename lib/plugins/create/templates/{aws-kotlin-node => aws-kotlin-nodejs-gradle}/src/main/kotlin/com/serverless/Handler.kt (100%) rename lib/plugins/create/templates/{aws-kotlin-node => aws-kotlin-nodejs-gradle}/src/main/kotlin/com/serverless/Response.kt (100%) rename lib/plugins/create/templates/{aws-kotlin-node => aws-kotlin-nodejs-gradle}/src/test/kotlin/.gitkeep (100%) diff --git a/docker-compose.yml b/docker-compose.yml index d674d4a94..5997745a6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -39,10 +39,10 @@ services: image: maven:3-jdk-8 volumes: - ./tmp/serverless-integration-test-aws-kotlin-jvm-maven:/app - aws-kotlin-node: + aws-kotlin-nodejs-gradle: image: pgoudreau/docker-maven-node volumes: - - ./tmp/serverless-integration-test-aws-kotlin-node:/app + - ./tmp/serverless-integration-test-aws-kotlin-nodejs-gradle:/app aws-groovy-gradle: image: java:8 volumes: diff --git a/docs/providers/aws/cli-reference/create.md b/docs/providers/aws/cli-reference/create.md index 38331142c..e88221851 100644 --- a/docs/providers/aws/cli-reference/create.md +++ b/docs/providers/aws/cli-reference/create.md @@ -46,7 +46,7 @@ Most commonly used templates: - aws-python - aws-python3 - aws-kotlin-jvm-maven -- aws-kotlin-node +- aws-kotlin-nodejs-gradle - aws-groovy-gradle - aws-java-maven - aws-java-gradle diff --git a/docs/providers/aws/guide/services.md b/docs/providers/aws/guide/services.md index 8d8575470..20a27a027 100644 --- a/docs/providers/aws/guide/services.md +++ b/docs/providers/aws/guide/services.md @@ -56,7 +56,7 @@ Here are the available runtimes for AWS Lambda: * aws-python * aws-python3 * aws-kotlin-jvm-maven -* aws-kotlin-node +* aws-kotlin-nodejs-gradle * aws-groovy-gradle * aws-java-gradle * aws-java-maven diff --git a/lib/plugins/create/create.js b/lib/plugins/create/create.js index 3cf8067f7..0bb965e98 100644 --- a/lib/plugins/create/create.js +++ b/lib/plugins/create/create.js @@ -17,7 +17,7 @@ const validTemplates = [ 'aws-java-maven', 'aws-java-gradle', 'aws-kotlin-jvm-maven', - 'aws-kotlin-node', + 'aws-kotlin-nodejs-gradle', 'aws-scala-sbt', 'aws-csharp', 'aws-fsharp', diff --git a/lib/plugins/create/create.test.js b/lib/plugins/create/create.test.js index 9d8e9ad2d..00567485e 100644 --- a/lib/plugins/create/create.test.js +++ b/lib/plugins/create/create.test.js @@ -228,9 +228,9 @@ describe('Create', () => { }); }); - it('should generate scaffolding for "aws-kotlin-node" template', () => { + it('should generate scaffolding for "aws-kotlin-nodejs-gradle" template', () => { process.chdir(tmpDir); - create.options.template = 'aws-kotlin-node'; + create.options.template = 'aws-kotlin-nodejs-gradle'; return create.create().then(() => { const dirContent = walkDirSync(tmpDir) @@ -240,6 +240,7 @@ describe('Create', () => { expect(dirContent).to.include('build.gradle'); expect(dirContent).to.include('gradlew'); expect(dirContent).to.include('gradlew.bat'); + expect(dirContent).to.include('package.json'); expect(dirContent).to.include(path.join('gradle', 'wrapper', 'gradle-wrapper.jar')); expect(dirContent).to.include(path.join('gradle', 'wrapper', diff --git a/lib/plugins/create/templates/aws-kotlin-node/build.gradle b/lib/plugins/create/templates/aws-kotlin-nodejs-gradle/build.gradle similarity index 100% rename from lib/plugins/create/templates/aws-kotlin-node/build.gradle rename to lib/plugins/create/templates/aws-kotlin-nodejs-gradle/build.gradle diff --git a/lib/plugins/create/templates/aws-kotlin-node/gitignore b/lib/plugins/create/templates/aws-kotlin-nodejs-gradle/gitignore similarity index 100% rename from lib/plugins/create/templates/aws-kotlin-node/gitignore rename to lib/plugins/create/templates/aws-kotlin-nodejs-gradle/gitignore diff --git a/lib/plugins/create/templates/aws-kotlin-node/gradle/wrapper/gradle-wrapper.jar b/lib/plugins/create/templates/aws-kotlin-nodejs-gradle/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from lib/plugins/create/templates/aws-kotlin-node/gradle/wrapper/gradle-wrapper.jar rename to lib/plugins/create/templates/aws-kotlin-nodejs-gradle/gradle/wrapper/gradle-wrapper.jar diff --git a/lib/plugins/create/templates/aws-kotlin-node/gradle/wrapper/gradle-wrapper.properties b/lib/plugins/create/templates/aws-kotlin-nodejs-gradle/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from lib/plugins/create/templates/aws-kotlin-node/gradle/wrapper/gradle-wrapper.properties rename to lib/plugins/create/templates/aws-kotlin-nodejs-gradle/gradle/wrapper/gradle-wrapper.properties diff --git a/lib/plugins/create/templates/aws-kotlin-node/gradlew b/lib/plugins/create/templates/aws-kotlin-nodejs-gradle/gradlew similarity index 100% rename from lib/plugins/create/templates/aws-kotlin-node/gradlew rename to lib/plugins/create/templates/aws-kotlin-nodejs-gradle/gradlew diff --git a/lib/plugins/create/templates/aws-kotlin-node/gradlew.bat b/lib/plugins/create/templates/aws-kotlin-nodejs-gradle/gradlew.bat similarity index 100% rename from lib/plugins/create/templates/aws-kotlin-node/gradlew.bat rename to lib/plugins/create/templates/aws-kotlin-nodejs-gradle/gradlew.bat diff --git a/lib/plugins/create/templates/aws-kotlin-node/package.json b/lib/plugins/create/templates/aws-kotlin-nodejs-gradle/package.json similarity index 100% rename from lib/plugins/create/templates/aws-kotlin-node/package.json rename to lib/plugins/create/templates/aws-kotlin-nodejs-gradle/package.json diff --git a/lib/plugins/create/templates/aws-kotlin-node/serverless.yml b/lib/plugins/create/templates/aws-kotlin-nodejs-gradle/serverless.yml similarity index 97% rename from lib/plugins/create/templates/aws-kotlin-node/serverless.yml rename to lib/plugins/create/templates/aws-kotlin-nodejs-gradle/serverless.yml index 4cb7f89d2..c590ec2e2 100644 --- a/lib/plugins/create/templates/aws-kotlin-node/serverless.yml +++ b/lib/plugins/create/templates/aws-kotlin-nodejs-gradle/serverless.yml @@ -11,7 +11,7 @@ # # Happy Coding! -service: aws-kotlin-node # NOTE: update this with your service name +service: aws-kotlin-nodejs-gradle # NOTE: update this with your service name # You can pin your service to only deploy with a specific Serverless version # Check out our docs for more details diff --git a/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/ApiGatewayResponse.kt b/lib/plugins/create/templates/aws-kotlin-nodejs-gradle/src/main/kotlin/com/serverless/ApiGatewayResponse.kt similarity index 100% rename from lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/ApiGatewayResponse.kt rename to lib/plugins/create/templates/aws-kotlin-nodejs-gradle/src/main/kotlin/com/serverless/ApiGatewayResponse.kt diff --git a/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Handler.kt b/lib/plugins/create/templates/aws-kotlin-nodejs-gradle/src/main/kotlin/com/serverless/Handler.kt similarity index 100% rename from lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Handler.kt rename to lib/plugins/create/templates/aws-kotlin-nodejs-gradle/src/main/kotlin/com/serverless/Handler.kt diff --git a/lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Response.kt b/lib/plugins/create/templates/aws-kotlin-nodejs-gradle/src/main/kotlin/com/serverless/Response.kt similarity index 100% rename from lib/plugins/create/templates/aws-kotlin-node/src/main/kotlin/com/serverless/Response.kt rename to lib/plugins/create/templates/aws-kotlin-nodejs-gradle/src/main/kotlin/com/serverless/Response.kt diff --git a/lib/plugins/create/templates/aws-kotlin-node/src/test/kotlin/.gitkeep b/lib/plugins/create/templates/aws-kotlin-nodejs-gradle/src/test/kotlin/.gitkeep similarity index 100% rename from lib/plugins/create/templates/aws-kotlin-node/src/test/kotlin/.gitkeep rename to lib/plugins/create/templates/aws-kotlin-nodejs-gradle/src/test/kotlin/.gitkeep diff --git a/tests/templates/test_all_templates b/tests/templates/test_all_templates index 6ad858980..66e47499b 100755 --- a/tests/templates/test_all_templates +++ b/tests/templates/test_all_templates @@ -18,7 +18,7 @@ integration-test aws-nodejs integration-test aws-python integration-test aws-python3 integration-test aws-kotlin-jvm-maven -integration-test aws-kotlin-node +integration-test aws-kotlin-nodejs-gradle integration-test aws-nodejs-typescript integration-test aws-nodejs-ecma-script integration-test google-nodejs From 57215c75d6bf0900c4398cbf9cc864d5e250a5ec Mon Sep 17 00:00:00 2001 From: David Wells Date: Fri, 15 Sep 2017 16:03:21 -0700 Subject: [PATCH 43/56] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 14f75373f..7bb50b46e 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,8 @@ Serverless is an MIT open-source project, actively maintained by a full-time, ve ## Quick Start + + [Watch the video guide here](https://serverless.com/framework/) or follow the steps below to create and deploy your first serverless microservice in minutes. 1. **Install via npm:** From 8158abba25d0cf3555f3fb1b050609ba7fe719b4 Mon Sep 17 00:00:00 2001 From: David Wells Date: Fri, 15 Sep 2017 16:05:55 -0700 Subject: [PATCH 44/56] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7bb50b46e..a11194278 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ Serverless is an MIT open-source project, actively maintained by a full-time, ve ## Contents + + * [Quick Start](#quick-start) * [Examples](https://github.com/serverless/examples) * [Services](#services) @@ -35,8 +37,6 @@ Serverless is an MIT open-source project, actively maintained by a full-time, ve ## Quick Start - - [Watch the video guide here](https://serverless.com/framework/) or follow the steps below to create and deploy your first serverless microservice in minutes. 1. **Install via npm:** From 98020dcce26c7ab3225b7edf5fd690e06db09fcc Mon Sep 17 00:00:00 2001 From: David Wells Date: Fri, 15 Sep 2017 16:07:01 -0700 Subject: [PATCH 45/56] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a11194278..a05009957 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Serverless is an MIT open-source project, actively maintained by a full-time, ve ## Contents - + * [Quick Start](#quick-start) * [Examples](https://github.com/serverless/examples) From 222461e2f325e07486383750fabf7098c734476e Mon Sep 17 00:00:00 2001 From: davidwells Date: Fri, 15 Sep 2017 16:12:12 -0700 Subject: [PATCH 46/56] update plugins and examples in readme --- README.md | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 14f75373f..43ea4581a 100644 --- a/README.md +++ b/README.md @@ -152,40 +152,60 @@ This table is generated from https://github.com/serverless/plugins/blob/master/p --> | Plugin | Author | |:-------|:------:| -| **[API GW binary support](https://github.com/maciejtreder/serverless-apigw-binary)**
Serverless plugin to enable binary support in AWS API Gateway. | [Maciej Treder](https://github.com/maciejtreder) | | **[Raml Serverless](https://github.com/andrewcurioso/raml-serverless)**
Serverless plugin to work with RAML API spec documents | [andrewcurioso](http://github.com/andrewcurioso) | | **[Serverless Alexa Plugin](https://github.com/rajington/serverless-alexa-plugin)**
Serverless plugin to support Alexa Lambda events | [rajington](http://github.com/rajington) | | **[Serverless Api Stage](https://github.com/leftclickben/serverless-api-stage)**
Serverless API Stage plugin, enables stage variables and logging for AWS API Gateway. | [leftclickben](http://github.com/leftclickben) | | **[Serverless Apig S3](https://github.com/sdd/serverless-apig-s3)**
Serve static front-end content from S3 via the API Gatewy and deploy client bundle to S3. | [sdd](http://github.com/sdd) | +| **[Serverless Apigateway Plugin](https://github.com/GFG/serverless-apigateway-plugin)**
Configure the AWS api gateway: Binary support, Headers and Body template mappings | [GFG](http://github.com/GFG) | +| **[Serverless Apigw Binary](https://github.com/maciejtreder/serverless-apigw-binary)**
Plugin to enable binary support in AWS API Gateway. | [maciejtreder](http://github.com/maciejtreder) | +| **[Serverless Apigwy Binary](https://github.com/ryanmurakami/serverless-apigwy-binary)**
Serverless plugin for configuring API Gateway to return binary responses | [ryanmurakami](http://github.com/ryanmurakami) | | **[Serverless Aws Alias](https://github.com/HyperBrain/serverless-aws-alias)**
This plugin enables use of AWS aliases on Lambda functions. | [HyperBrain](http://github.com/HyperBrain) | | **[Serverless Aws Documentation](https://github.com/9cookies/serverless-aws-documentation)**
Serverless plugin to add documentation and models to the serverless generated API Gateway | [9cookies](http://github.com/9cookies) | | **[Serverless Build Plugin](https://github.com/nfour/serverless-build-plugin)**
A Node.js focused build plugin for serverless. | [nfour](http://github.com/nfour) | +| **[Serverless Cf Vars](https://gitlab.com/kabo/serverless-cf-vars)**
Enables use of AWS pseudo functions and Fn::Sub string substitution | [kabo](http://github.com/kabo) | | **[Serverless Cljs Plugin](https://github.com/nervous-systems/serverless-cljs-plugin)**
Enables Clojurescript as an implementation language for Lambda handlers | [nervous-systems](http://github.com/nervous-systems) | | **[Serverless Coffeescript](https://github.com/duanefields/serverless-coffeescript)**
A Serverless plugin to compile your CoffeeScript into JavaScript at deployment | [duanefields](http://github.com/duanefields) | | **[Serverless Command Line Event Args](https://github.com/horike37/serverless-command-line-event-args)**
This module is Serverless Framework plugin. Event JSON passes to your Lambda function in commandline. | [horike37](http://github.com/horike37) | | **[Serverless Crypt](https://github.com/marcy-terui/serverless-crypt)**
Securing the secrets on Serverless Framework by AWS KMS encryption. | [marcy-terui](http://github.com/marcy-terui) | +| **[Serverless Custom Packaging Plugin](https://github.com/hypoport/serverless-custom-packaging-plugin)**
Plugin to package your sourcecode using a custom target path inside the zip. | [hypoport](http://github.com/hypoport) | | **[Serverless Dir Config Plugin](https://github.com/economysizegeek/serverless-dir-config-plugin)**
EXPERIMENTAL - Serverless plugin to load function and resource definitions from a directory. | [economysizegeek](http://github.com/economysizegeek) | +| **[Serverless Domain Manager](https://github.com/amplify-education/serverless-domain-manager)**
Serverless plugin for managing custom domains with API Gateways. | [amplify-education](http://github.com/amplify-education) | | **[Serverless Dotenv](https://github.com/Jimdo/serverless-dotenv)**
Fetch environment variables and write it to a .env file | [Jimdo](http://github.com/Jimdo) | | **[Serverless Dotnet](https://github.com/fruffin/serverless-dotnet)**
A serverless plugin to run 'dotnet' commands as part of the deploy process | [fruffin](http://github.com/fruffin) | | **[Serverless Dynalite](https://github.com/sdd/serverless-dynalite)**
Run dynalite locally (no JVM, all JS) to simulate DynamoDB. Watch serverless.yml for table config updates. | [sdd](http://github.com/sdd) | +| **[Serverless Dynamodb Autoscaling](https://github.com/sbstjn/serverless-dynamodb-autoscaling)**
Configure Amazon DynamoDB's native Auto Scaling for your table capacities. | [sbstjn](http://github.com/sbstjn) | | **[Serverless Dynamodb Local](https://github.com/99xt/serverless-dynamodb-local)**
Serverless Dynamodb Local Plugin - Allows to run dynamodb locally for serverless | [99xt](http://github.com/99xt) | | **[Serverless Dynamodb Ttl](https://github.com/Jimdo/serverless-dynamodb-ttl)**
Configure DynamoDB TTL in serverless.yml (until CloudFormation supports this). | [Jimdo](http://github.com/Jimdo) | | **[Serverless Enable Api Logs](https://github.com/paulSambolin/serverless-enable-api-logs)**
Enables Coudwatch logging for API Gateway events | [paulSambolin](http://github.com/paulSambolin) | +| **[Serverless Env Generator](https://github.com/DieProduktMacher/serverless-env-generator)**
Manage environment variables with YAML and load them with dotenv. Supports variable encryption with KMS, multiple stages and custom profiles. | [DieProduktMacher](http://github.com/DieProduktMacher) | | **[Serverless Event Constant Inputs](https://github.com/dittto/serverless-event-constant-inputs)**
Allows you to add constant inputs to events in Serverless 1.0. For more info see [constant values in Cloudwatch](https://aws.amazon.com/blogs/compute/simply-serverless-use-constant-values-in-cloudwatch-event-triggered-lambda-functions/) | [dittto](http://github.com/dittto) | | **[Serverless Export Env](https://github.com/arabold/serverless-export-env)**
Export environment variables into a .env file with automatic AWS CloudFormation reference resolution. | [arabold](http://github.com/arabold) | +| **[Serverless Gulp](https://github.com/rhythminme/serverless-gulp)**
A thin task wrapper around @goserverless that allows you to automate build, test and deploy tasks using gulp | [rhythminme](http://github.com/rhythminme) | | **[Serverless Hooks Plugin](https://github.com/uswitch/serverless-hooks-plugin)**
Run arbitrary commands on any lifecycle event in serverless | [uswitch](http://github.com/uswitch) | | **[Serverless Jest Plugin](https://github.com/SC5/serverless-jest-plugin)**
A Serverless Plugin for the Serverless Framework which adds support for test-driven development using Jest | [SC5](http://github.com/SC5) | +| **[Serverless Kms Secrets](https://github.com/SC5/serverless-kms-secrets)**
Allows to easily encrypt and decrypt secrets using KMS from the serverless CLI | [SC5](http://github.com/SC5) | +| **[Serverless Kubeless](https://github.com/serverless/serverless-kubeless)**
Serverless plugin for deploying functions to Kubeless. | [serverless](http://github.com/serverless) | +| **[Serverless Local Dev Server](https://github.com/DieProduktMacher/serverless-local-dev-server)**
Speeds up development of Alexa Skills, Chatbots and APIs by exposing your functions as local HTTP endpoints and mapping received events. | [DieProduktMacher](http://github.com/DieProduktMacher) | +| **[Serverless Log Forwarding](https://github.com/amplify-education/serverless-log-forwarding)**
Serverless plugin for forwarding CloudWatch logs to another Lambda function. | [amplify-education](http://github.com/amplify-education) | | **[Serverless Mocha Plugin](https://github.com/SC5/serverless-mocha-plugin)**
A Serverless Plugin for the Serverless Framework which adds support for test-driven development using Mocha | [SC5](http://github.com/SC5) | +| **[Serverless Nested Stack](https://github.com/jagdish-176/serverless-nested-stack)**
A plugin to Workaround for Cloudformation 200 resource limit | [jagdish-176](http://github.com/jagdish-176) | | **[Serverless Offline](https://github.com/dherault/serverless-offline)**
Emulate AWS λ and API Gateway locally when developing your Serverless project | [dherault](http://github.com/dherault) | | **[Serverless Offline Scheduler](https://github.com/ajmath/serverless-offline-scheduler)**
Runs scheduled functions offline while integrating with serverless-offline | [ajmath](http://github.com/ajmath) | | **[Serverless Package Python Functions](https://github.com/ubaniabalogun/serverless-package-python-functions)**
Packaging Python Lambda functions with only the dependencies/requirements they need. | [ubaniabalogun](http://github.com/ubaniabalogun) | | **[Serverless Parameters](https://github.com/svdgraaf/serverless-parameters)**
Add parameters to the generated cloudformation templates | [svdgraaf](http://github.com/svdgraaf) | | **[Serverless Plugin Aws Alerts](https://github.com/ACloudGuru/serverless-plugin-aws-alerts)**
A Serverless plugin to easily add CloudWatch alarms to functions | [ACloudGuru](http://github.com/ACloudGuru) | +| **[Serverless Plugin Aws Resolvers](https://github.com/DopplerLabs/serverless-plugin-aws-resolvers)**
Resolves variables from ESS, RDS, or Kinesis for serverless services | [DopplerLabs](http://github.com/DopplerLabs) | +| **[Serverless Plugin Bespoken](https://github.com/bespoken/serverless-plugin-bespoken)**
Creates a local server and a proxy so you don't have to deploy anytime you want to test your code | [bespoken](http://github.com/bespoken) | | **[Serverless Plugin Bind Deployment Id](https://github.com/jacob-meacham/serverless-plugin-bind-deployment-id)**
A Serverless plugin to bind the randomly generated deployment resource to your custom resources | [jacob-meacham](http://github.com/jacob-meacham) | +| **[Serverless Plugin Browserifier](https://github.com/digitalmaas/serverless-plugin-browserifier)**
Reduce the size and speed up your Node.js based lambda's using browserify. | [digitalmaas](http://github.com/digitalmaas) | | **[Serverless Plugin Browserify](https://github.com/doapp-ryanp/serverless-plugin-browserify)**
Speed up your node based lambda's | [doapp-ryanp](http://github.com/doapp-ryanp) | | **[Serverless Plugin Cfauthorizer](https://github.com/SC5/serverless-plugin-cfauthorizer)**
This plugin allows you to define your own API Gateway Authorizers as the Serverless CloudFormation resources and apply them to HTTP endpoints. | [SC5](http://github.com/SC5) | | **[Serverless Plugin Cloudwatch Sumologic](https://github.com/ACloudGuru/serverless-plugin-cloudwatch-sumologic)**
Plugin which auto-subscribes a log delivery lambda function to lambda log groups created by serverless | [ACloudGuru](http://github.com/ACloudGuru) | +| **[Serverless Plugin Common Excludes](https://github.com/dougmoscrop/serverless-plugin-common-excludes)**
Adds commonly excluded files to package.excludes | [dougmoscrop](http://github.com/dougmoscrop) | +| **[Serverless Plugin Custom Domain](https://github.com/dougmoscrop/serverless-plugin-custom-domain)**
Reliably sets a BasePathMapping to an API Gateway Custom Domain | [dougmoscrop](http://github.com/dougmoscrop) | +| **[Serverless Plugin Deploy Environment](https://github.com/DopplerLabs/serverless-plugin-deploy-environment)**
Plugin to manage deployment environment across stages | [DopplerLabs](http://github.com/DopplerLabs) | | **[Serverless Plugin Diff](https://github.com/nicka/serverless-plugin-diff)**
Compares your local AWS CloudFormation templates against deployed ones. | [nicka](http://github.com/nicka) | +| **[Serverless Plugin Elastic Beanstalk](https://github.com/rawphp/serverless-plugin-elastic-beanstalk)**
A serverless plugin to deploy applications to AWS ElasticBeanstalk. | [rawphp](http://github.com/rawphp) | | **[Serverless Plugin Encode Env Var Objects](https://github.com/yonomi/serverless-plugin-encode-env-var-objects)**
Serverless plugin to encode any environment variable objects. | [yonomi](http://github.com/yonomi) | | **[Serverless Plugin External Sns Events](https://github.com/silvermine/serverless-plugin-external-sns-events)**
Add ability for functions to use existing or external SNS topics as an event source | [silvermine](http://github.com/silvermine) | | **[Serverless Plugin Git Variables](https://github.com/jacob-meacham/serverless-plugin-git-variables)**
A Serverless plugin to expose git variables (branch name, HEAD description, full commit hash) to your serverless services | [jacob-meacham](http://github.com/jacob-meacham) | @@ -193,12 +213,16 @@ This table is generated from https://github.com/serverless/plugins/blob/master/p | **[Serverless Plugin Include Dependencies](https://github.com/dougmoscrop/serverless-plugin-include-dependencies)**
This is a Serverless plugin that should make your deployed functions smaller. | [dougmoscrop](http://github.com/dougmoscrop) | | **[Serverless Plugin Iopipe](https://github.com/iopipe/serverless-plugin-iopipe)**
See inside your Lambda functions with high fidelity metrics and monitoring. | [iopipe](http://github.com/iopipe) | | **[Serverless Plugin Lambda Dead Letter](https://github.com/gmetzker/serverless-plugin-lambda-dead-letter)**
A Serverless plugin that can configure a lambda with a dead letter queue or topic | [gmetzker](http://github.com/gmetzker) | +| **[Serverless Plugin Log Subscription](https://github.com/dougmoscrop/serverless-plugin-log-subscription)**
Adds a CloudWatch LogSubscription for functions | [dougmoscrop](http://github.com/dougmoscrop) | | **[Serverless Plugin Multiple Responses](https://github.com/silvermine/serverless-plugin-multiple-responses)**
Enable multiple content-types for Response template | [silvermine](http://github.com/silvermine) | +| **[Serverless Plugin Offline Kinesis Events](https://github.com/DopplerLabs/serverless-plugin-offline-kinesis-events)**
Plugin that works with serverless-offline to allow offline testing of serverless functions that are triggered by Kinesis events. | [DopplerLabs](http://github.com/DopplerLabs) | | **[Serverless Plugin Optimize](https://github.com/FidelLimited/serverless-plugin-optimize)**
Bundle with Browserify, transpile with Babel to ES5 and minify with Uglify your Serverless functions. | [FidelLimited](http://github.com/FidelLimited) | | **[Serverless Plugin Package Dotenv File](https://github.com/ACloudGuru/serverless-plugin-package-dotenv-file)**
A Serverless plugin to copy a .env file into the serverless package | [ACloudGuru](http://github.com/ACloudGuru) | | **[Serverless Plugin Scripts](https://github.com/mvila/serverless-plugin-scripts)**
Add scripting capabilities to the Serverless Framework | [mvila](http://github.com/mvila) | | **[Serverless Plugin Select](https://github.com/FidelLimited/serverless-plugin-select)**
Select which functions are to be deployed based on region and stage. | [FidelLimited](http://github.com/FidelLimited) | | **[Serverless Plugin Simulate](https://github.com/gertjvr/serverless-plugin-simulate)**
Simulate AWS Lambda and API Gateway locally using Docker | [gertjvr](http://github.com/gertjvr) | +| **[Serverless Plugin Split Stacks](https://github.com/dougmoscrop/serverless-plugin-split-stacks)**
Migrate certain resources to nested stacks | [dougmoscrop](http://github.com/dougmoscrop) | +| **[Serverless Plugin Stack Config](https://github.com/rawphp/serverless-plugin-stack-config)**
A serverless plugin to manage configurations for a stack across micro-services. | [rawphp](http://github.com/rawphp) | | **[Serverless Plugin Stack Outputs](https://github.com/svdgraaf/serverless-plugin-stack-outputs)**
Displays stack outputs for your serverless stacks when `sls info` is ran | [svdgraaf](http://github.com/svdgraaf) | | **[Serverless Plugin Stage Variables](https://github.com/svdgraaf/serverless-plugin-stage-variables)**
Add stage variables for Serverless 1.x to ApiGateway, so you can use variables in your Lambda's | [svdgraaf](http://github.com/svdgraaf) | | **[Serverless Plugin Subscription Filter](https://github.com/tsub/serverless-plugin-subscription-filter)**
A serverless plugin to register AWS CloudWatchLogs subscription filter | [tsub](http://github.com/tsub) | @@ -212,14 +236,20 @@ This table is generated from https://github.com/serverless/plugins/blob/master/p | **[Serverless Python Requirements](https://github.com/UnitedIncome/serverless-python-requirements)**
Serverless plugin to bundle Python packages | [UnitedIncome](http://github.com/UnitedIncome) | | **[Serverless Resources Env](https://github.com/rurri/serverless-resources-env)**
After Deploy, this plugin fetches cloudformation resource identifiers and sets them on AWS lambdas, and creates local .-env file | [rurri](http://github.com/rurri) | | **[Serverless Run Function Plugin](https://github.com/lithin/serverless-run-function-plugin)**
Run serverless function locally | [lithin](http://github.com/lithin) | +| **[Serverless S3 Remover](https://github.com/sinofseven/serverless-s3-remover)**
A serverless plugin to make s3 buckets empty before deleting cloudformation stack when ```sls remove``` | [sinofseven](http://github.com/sinofseven) | +| **[Serverless S3 Sync](https://github.com/k1LoW/serverless-s3-sync)**
A plugin to sync local directories and S3 prefixes for Serverless Framework, | [k1LoW](http://github.com/k1LoW) | +| **[Serverless S3bucket Sync](https://github.com/sbstjn/serverless-s3bucket-sync)**
Sync a local folder with a S3 bucket after sls deploy | [sbstjn](http://github.com/sbstjn) | | **[Serverless Sam](https://github.com/SAPessi/serverless-sam)**
Exports an AWS SAM template for a service created with the Serverless Framework. | [SAPessi](http://github.com/SAPessi) | | **[Serverless Scriptable Plugin](https://github.com/weixu365/serverless-scriptable-plugin)**
Customize Serverless behavior without writing a plugin. | [weixu365](http://github.com/weixu365) | +| **[Serverless Sentry](https://github.com/arabold/serverless-sentry-plugin)**
Automatic monitoring of memory usage, execution timeouts and forwarding of Lambda errors to Sentry (https://sentry.io). | [arabold](http://github.com/arabold) | +| **[Serverless Shell](https://github.com/UnitedIncome/serverless-shell)**
Drop to a runtime shell with all the environment variables set that you'd have in lambda. | [UnitedIncome](http://github.com/UnitedIncome) | | **[Serverless Sqs Alarms Plugin](https://github.com/sbstjn/serverless-sqs-alarms-plugin)**
Wrapper to setup CloudWatch Alarms on SQS queue length | [sbstjn](http://github.com/sbstjn) | | **[Serverless Sqs Fifo](https://github.com/vortarian/serverless-sqs-fifo)**
A serverless plugin to handle creation of sqs fifo queue's in aws (stop-gap) | [vortarian](http://github.com/vortarian) | +| **[Serverless Stack Output](https://github.com/sbstjn/serverless-stack-output)**
Store output from your AWS CloudFormation Stack in JSON/YAML/TOML files, or to pass it to a JavaScript function for further processing. | [sbstjn](http://github.com/sbstjn) | | **[Serverless Step Functions](https://github.com/horike37/serverless-step-functions)**
AWS Step Functions with Serverless Framework. | [horike37](http://github.com/horike37) | | **[Serverless Subscription Filter](https://github.com/blackevil245/serverless-subscription-filter)**
Serverless plugin to register subscription filter for Lambda logs. Register and pipe the logs of one lambda to another to process. | [blackevil245](http://github.com/blackevil245) | | **[Serverless Vpc Discovery](https://github.com/amplify-education/serverless-vpc-discovery)**
Serverless plugin for discovering VPC / Subnet / Security Group configuration by name. | [amplify-education](http://github.com/amplify-education) | -| **[Serverless Webpack](https://github.com/elastic-coders/serverless-webpack)**
Serverless plugin to bundle your lambdas with Webpack | [elastic-coders](http://github.com/elastic-coders) | +| **[Serverless Webpack](https://github.com/serverless-heaven/serverless-webpack)**
Serverless plugin to bundle your lambdas with Webpack | [serverless-heaven](http://github.com/serverless-heaven) | | **[Serverless Wsgi](https://github.com/logandk/serverless-wsgi)**
Serverless plugin to deploy WSGI applications (Flask/Django/Pyramid etc.) and bundle Python packages | [logandk](http://github.com/logandk) | @@ -230,6 +260,7 @@ This table is generated from https://github.com/serverless/examples/blob/master/ --> | Project Name | Author | |:-------------|:------:| +| **[Jwtauthorizr](https://github.com/serverlessbuch/jwtAuthorizr)**
Custom JWT Authorizer Lambda function for Amazon API Gateway with Bearer JWT | [serverlessbuch](http://github.com/serverlessbuch) | | **[Serverless Graphql Api](https://github.com/boazdejong/serverless-graphql-api)**
Serverless GraphQL API using Lambda and DynamoDB | [boazdejong](http://github.com/boazdejong) | | **[Serverless Screenshot](https://github.com/svdgraaf/serverless-screenshot)**
Serverless Screenshot Service using PhantomJS | [svdgraaf](http://github.com/svdgraaf) | | **[Serverless Postgraphql](https://github.com/rentrop/serverless-postgraphql)**
GraphQL endpoint for PostgreSQL using postgraphql | [rentrop](http://github.com/rentrop) | @@ -275,6 +306,14 @@ This table is generated from https://github.com/serverless/examples/blob/master/ | **[Aws Api Gateway Serverless Project Written In Go](https://github.com/yunspace/serverless-golang)**
A serverless project that contains an API Gateway endpoint powered by a Lambda function written in golang and built using [eawsy/aws-lambda-go-shim](https://github.com/eawsy/aws-lambda-go-shim). | [yunspace](http://github.com/yunspace) | | **[Video Preview And Analysis Service](https://github.com/laardee/video-preview-and-analysis-service)**
An event-driven service that generates labels using Amazon Rekognition and creates preview GIF animation from a video file. | [laardee](http://github.com/laardee) | | **[Serverless Es6/7 Crud Api](https://github.com/AnomalyInnovations/serverless-stack-demo-api)**
[Serverless Stack](http://serverless-stack.com) examples of backend CRUD APIs (DynamoDB + Lambda + API Gateway + Cognito User Pool authorizer) for [React.js single-page app](http://demo.serverless-stack.com) | [AnomalyInnovations](http://github.com/AnomalyInnovations) | +| **[Sqs Worker With Aws Lambda And Cloudwatch Alarms](https://github.com/sbstjn/sqs-worker-serverless)**
Process messages stored in SQS with an [auto-scaled AWS Lambda worker](https://sbstjn.com/serverless-sqs-worker-with-aws-lambda.html) function. | [sbstjn](http://github.com/sbstjn) | +| **[Aws Lambda Power Tuning (Powered By Step Functions)](https://github.com/alexcasalboni/aws-lambda-power-tuning)**
Build a [Step Functions](https://aws.amazon.com/step-functions/) state machine to optimize your AWS Lambda Function memory/power configuration. | [alexcasalboni](http://github.com/alexcasalboni) | +| **[Amazon Kinesis Streams Fan Out Via Kinesis Analytics](https://github.com/alexcasalboni/kinesis-streams-fan-out-kinesis-analytics)**
Use [Amazon Kinesis Analytics](https://aws.amazon.com/kinesis/analytics/) to fan-out your Kinesis Streams and avoid read throttling. | [alexcasalboni](http://github.com/alexcasalboni) | +| **[Grants Api Serverless](https://github.com/comicrelief/grants-api-serverless)**
ES6 API to consume data from an external API, ingest into Elasticsearch and return a queryable endpoint on top of Elasticsearch | [comicrelief](http://github.com/comicrelief) | +| **[Honeylambda](https://github.com/0x4D31/honeyLambda)**
a simple, serverless application designed to create and monitor URL {honey}tokens, on top of AWS Lambda and Amazon API Gateway | [0x4D31](http://github.com/0x4D31) | +| **[Stack Overflow Monitor](https://github.com/picsoung/stackoverflowmonitor)**
Monitor Stack Overflow questions and post them in a Slack channel | [picsoung](http://github.com/picsoung) | +| **[React & Stripe Serverless Ecommerce](https://github.com/patrick-michelberger/serverless-shop)**
Serverless E-Commerce App with AWS Lambda, Stripe and React | [patrick-michelberger](http://github.com/patrick-michelberger) | +| **[Serverless + Medium Text To Speech](https://github.com/RafalWilinski/serverless-medium-text-to-speech)**
Serverless-based, text-to-speech service for Medium articles | [RafalWilinski](http://github.com/RafalWilinski) | ## Contributing From b0b7cdce110fe5665df5fc4ac5d6f62962d2e25c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Garen=20Y=C3=B6ndem?= Date: Sat, 16 Sep 2017 22:09:38 +0300 Subject: [PATCH 47/56] Added serverless-azure-functions to pre-requisites Serverless deploy throws error without serverless-azure-functions plugin installed globally. --- docs/providers/azure/guide/quick-start.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/providers/azure/guide/quick-start.md b/docs/providers/azure/guide/quick-start.md index d77a97a18..2e51d831b 100644 --- a/docs/providers/azure/guide/quick-start.md +++ b/docs/providers/azure/guide/quick-start.md @@ -13,8 +13,9 @@ layout: Doc 1. Node.js `v6.5.0` or later. *(this is the runtime version supported by Azure Functions)* 2. Serverless CLI `v1.9.0` or later. You can run `npm install -g serverless` to install it. -3. An Azure account. If you don't already have one, you can sign up for a [free trial](https://azure.microsoft.com/en-us/free/) that includes $200 of free credit. -4. **Set-up your [Provider Credentials](./credentials.md)**. +3. Azure plugin that allows you to work with Azure Functions `npm install -g serverless-azure-functions` +4. An Azure account. If you don't already have one, you can sign up for a [free trial](https://azure.microsoft.com/en-us/free/) that includes $200 of free credit. +5. **Set-up your [Provider Credentials](./credentials.md)**. ## Create a new service From 646a9383ee18c14d1858354e2091f1e9e181bbbc Mon Sep 17 00:00:00 2001 From: Philipp Muens Date: Mon, 18 Sep 2017 10:02:47 +0200 Subject: [PATCH 48/56] Update console.log usage to use utils log function --- lib/utils/log/serverlessLog.js | 11 +++++++++++ lib/utils/yamlAstParser.js | 7 ++++++- 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 lib/utils/log/serverlessLog.js diff --git a/lib/utils/log/serverlessLog.js b/lib/utils/log/serverlessLog.js new file mode 100644 index 000000000..94ba61795 --- /dev/null +++ b/lib/utils/log/serverlessLog.js @@ -0,0 +1,11 @@ +'use strict'; + +/* eslint-disable no-console */ + +const chalk = require('chalk'); + +const log = function (message) { + console.log(`Serverless: ${chalk.yellow(message)}`); +}; + +module.exports = log; diff --git a/lib/utils/yamlAstParser.js b/lib/utils/yamlAstParser.js index a86c7d4dd..b1fa0a120 100644 --- a/lib/utils/yamlAstParser.js +++ b/lib/utils/yamlAstParser.js @@ -6,6 +6,7 @@ const fs = BbPromise.promisifyAll(require('fs')); const _ = require('lodash'); const os = require('os'); const chalk = require('chalk'); +const log = require('./log/serverlessLog'); const findKeyChain = (astContent) => { let content = astContent; @@ -26,7 +27,11 @@ const parseAST = (ymlAstContent, astObject) => { if (ymlAstContent.mappings && _.isArray(ymlAstContent.mappings)) { _.forEach(ymlAstContent.mappings, (v) => { if (!v.value) { - console.log(`Serverless: ${chalk.red(`Your serverless.yml has an invalid value with key: ${v.key.value}. Ignoring...`)}`); + const errorMessage = [ + 'Serverless: ', + `${chalk.red('Your serverless.yml has an invalid value with key:', v.key.value)}`, + ].join(''); + log(errorMessage); return; } From ef1ce9ec0006b2c0b80495eedba455bb40fff71e Mon Sep 17 00:00:00 2001 From: Philipp Muens Date: Mon, 18 Sep 2017 10:08:21 +0200 Subject: [PATCH 49/56] Add "" around key name for a better visual representation --- lib/utils/yamlAstParser.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/utils/yamlAstParser.js b/lib/utils/yamlAstParser.js index b1fa0a120..7d91f55f9 100644 --- a/lib/utils/yamlAstParser.js +++ b/lib/utils/yamlAstParser.js @@ -29,7 +29,8 @@ const parseAST = (ymlAstContent, astObject) => { if (!v.value) { const errorMessage = [ 'Serverless: ', - `${chalk.red('Your serverless.yml has an invalid value with key:', v.key.value)}`, + `${chalk.red('Your serverless.yml has an invalid value with key:')} `, + `${chalk.red(`"${v.key.value}"`)}`, ].join(''); log(errorMessage); return; From ff03b89c2f3481381c875911b0bc26a3b3972856 Mon Sep 17 00:00:00 2001 From: Philipp Muens Date: Mon, 18 Sep 2017 11:06:14 +0200 Subject: [PATCH 50/56] Minor fixes to avoid race conditions --- lib/plugins/aws/invokeLocal/index.js | 53 +++++++++++------------ lib/plugins/aws/invokeLocal/index.test.js | 26 ++++------- 2 files changed, 34 insertions(+), 45 deletions(-) diff --git a/lib/plugins/aws/invokeLocal/index.js b/lib/plugins/aws/invokeLocal/index.js index 4996f6402..37887cd36 100644 --- a/lib/plugins/aws/invokeLocal/index.js +++ b/lib/plugins/aws/invokeLocal/index.js @@ -2,7 +2,7 @@ const BbPromise = require('bluebird'); const _ = require('lodash'); -const fs = BbPromise.promisifyAll(require('graceful-fs')); +const fs = BbPromise.promisifyAll(require('fs')); const path = require('path'); const validate = require('../lib/validate'); const chalk = require('chalk'); @@ -172,29 +172,27 @@ class AwsInvokeLocal { } callJavaBridge(artifactPath, className, input) { - return new BbPromise((resolve) => { - fs.statAsync(artifactPath).then(() => { - const java = spawn('java', [ - `-DartifactPath=${artifactPath}`, - `-DclassName=${className}`, - '-jar', - path.join(__dirname, 'java', 'target', 'invoke-bridge-1.0.jar'), - ]); + return new BbPromise((resolve) => fs.statAsync(artifactPath).then(() => { + const java = spawn('java', [ + `-DartifactPath=${artifactPath}`, + `-DclassName=${className}`, + '-jar', + path.join(__dirname, 'java', 'target', 'invoke-bridge-1.0.jar'), + ]); - this.serverless.cli.log([ - 'In order to get human-readable output,', - 'please implement "toString()" method of your "ApiGatewayResponse" object.', - ].join(' ')); + this.serverless.cli.log([ + 'In order to get human-readable output,', + 'please implement "toString()" method of your "ApiGatewayResponse" object.', + ].join(' ')); - java.stdout.on('data', (buf) => this.serverless.cli.consoleLog(buf.toString())); - java.stderr.on('data', (buf) => this.serverless.cli.consoleLog(buf.toString())); - java.stdin.write(input); - java.stdin.end(); - java.on('close', () => resolve()); - }).catch(() => { - throw new Error(`Artifact ${artifactPath} doesn't exists, please compile it first.`); - }); - }); + java.stdout.on('data', (buf) => this.serverless.cli.consoleLog(buf.toString())); + java.stderr.on('data', (buf) => this.serverless.cli.consoleLog(buf.toString())); + java.stdin.write(input); + java.stdin.end(); + java.on('close', () => resolve()); + }).catch(() => { + throw new Error(`Artifact ${artifactPath} doesn't exists, please compile it first.`); + })); } invokeLocalJava(runtime, className, artifactPath, event, customContext) { @@ -215,10 +213,10 @@ class AwsInvokeLocal { const javaBridgePath = path.join(__dirname, 'java'); const executablePath = path.join(javaBridgePath, 'target'); - return new BbPromise(resolve => { - fs.statAsync(executablePath).then(() => { - this.callJavaBridge(artifactPath, className, input).then(resolve); - }).catch(() => { + return new BbPromise(resolve => fs.statAsync(executablePath) + .then(() => this.callJavaBridge(artifactPath, className, input)) + .then(resolve) + .catch(() => { const mvn = spawn('mvn', [ 'package', '-f', @@ -235,8 +233,7 @@ class AwsInvokeLocal { mvn.on('close', () => { this.callJavaBridge(artifactPath, className, input).then(resolve); }); - }); - }); + })); } invokeLocalNodeJs(handlerPath, handlerName, event, customContext) { diff --git a/lib/plugins/aws/invokeLocal/index.test.js b/lib/plugins/aws/invokeLocal/index.test.js index 79655d1e5..8e73d67ec 100644 --- a/lib/plugins/aws/invokeLocal/index.test.js +++ b/lib/plugins/aws/invokeLocal/index.test.js @@ -3,11 +3,9 @@ const expect = require('chai').expect; const sinon = require('sinon'); const path = require('path'); -const BbPromise = require('bluebird'); const mockRequire = require('mock-require'); const EventEmitter = require('events'); -const fs = BbPromise.promisifyAll(require('graceful-fs')); -const fsExtra = require('fs-extra'); +const fse = require('fs-extra'); const AwsInvokeLocal = require('./index'); const AwsProvider = require('../provider/awsProvider'); const Serverless = require('../../../Serverless'); @@ -531,7 +529,7 @@ describe('AwsInvokeLocal', () => { }); }); - describe('#callJavaBridge', () => { + describe('#callJavaBridge()', () => { let awsInvokeLocalMocked; let writeChildStub; let endChildStub; @@ -591,11 +589,12 @@ describe('AwsInvokeLocal', () => { ); }); - describe('#invokeLocalJava', () => { + describe('#invokeLocalJava()', () => { const bridgePath = path.join(__dirname, 'java', 'target'); let callJavaBridgeStub; beforeEach(() => { + fse.mkdirsSync(bridgePath); callJavaBridgeStub = sinon.stub(awsInvokeLocal, 'callJavaBridge').resolves(); awsInvokeLocal.options = { stage: 'dev', @@ -607,20 +606,14 @@ describe('AwsInvokeLocal', () => { }, data: {}, }; - - serverless.cli = new CLI(serverless); - sinon.stub(serverless.cli, 'consoleLog'); }); afterEach(() => { - serverless.cli.consoleLog.restore(); awsInvokeLocal.callJavaBridge.restore(); - fsExtra.removeSync(bridgePath); + fse.removeSync(bridgePath); }); - it('should invoke callJavaBridge when bridge is built', () => { - fs.mkdirSync(bridgePath); - + it('should invoke callJavaBridge when bridge is built', () => awsInvokeLocal.invokeLocalJava( 'java', 'com.serverless.Handler', @@ -641,10 +634,10 @@ describe('AwsInvokeLocal', () => { }, }) )).to.be.equal(true); - }); - }); + }) + ); - describe('should build Java bridge', () => { + describe('when attempting to build the Java bridge', () => { let awsInvokeLocalMocked; let callJavaBridgeMockedStub; @@ -696,7 +689,6 @@ describe('AwsInvokeLocal', () => { __dirname, {} ).then(() => { - expect(serverless.cli.consoleLog.lastCall.args[0]).to.contain('Building Java bridge, first invocation might take a bit longer.'); //eslint-disable-line expect(callJavaBridgeMockedStub.calledOnce).to.be.equal(true); expect(callJavaBridgeMockedStub.calledWithExactly( __dirname, From a1cf9fd65edbda214196e090366c6a73def08568 Mon Sep 17 00:00:00 2001 From: Philipp Muens Date: Mon, 18 Sep 2017 12:29:37 +0200 Subject: [PATCH 51/56] Minor fixes --- lib/plugins/aws/invokeLocal/index.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/lib/plugins/aws/invokeLocal/index.js b/lib/plugins/aws/invokeLocal/index.js index 37887cd36..2bdb17365 100644 --- a/lib/plugins/aws/invokeLocal/index.js +++ b/lib/plugins/aws/invokeLocal/index.js @@ -182,8 +182,8 @@ class AwsInvokeLocal { this.serverless.cli.log([ 'In order to get human-readable output,', - 'please implement "toString()" method of your "ApiGatewayResponse" object.', - ].join(' ')); + ' please implement "toString()" method of your "ApiGatewayResponse" object.', + ].join('')); java.stdout.on('data', (buf) => this.serverless.cli.consoleLog(buf.toString())); java.stderr.on('data', (buf) => this.serverless.cli.consoleLog(buf.toString())); @@ -223,16 +223,13 @@ class AwsInvokeLocal { path.join(javaBridgePath, 'pom.xml'), ]); - this.serverless.cli.log( - 'Building Java bridge, first invocation might take a bit longer.' - ); + this.serverless.cli + .log('Building Java bridge, first invocation might take a bit longer.'); mvn.stderr.on('data', (buf) => this.serverless.cli.consoleLog(`mvn - ${buf.toString()}`)); mvn.stdin.end(); - mvn.on('close', () => { - this.callJavaBridge(artifactPath, className, input).then(resolve); - }); + mvn.on('close', () => this.callJavaBridge(artifactPath, className, input).then(resolve)); })); } From e36200a7f575bf1f1ea5f2f75135d2da515334b9 Mon Sep 17 00:00:00 2001 From: Rafal Wilinski Date: Mon, 18 Sep 2017 12:51:10 +0200 Subject: [PATCH 52/56] Add more supported languages to README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 75e4c2404..03f42434d 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,7 @@ The following are services you can instantly install and use by running `serverl ## Features -* Supports Node.js, Python, Java & Scala. +* Supports Node.js, Python, Java, Scala, C#, F#, Groovy, Kotlin, PHP & Swift. * Manages the lifecycle of your serverless architecture (build, deploy, update, delete). * Safely deploy functions, events and their required resources together via provider resource managers (e.g., AWS CloudFormation). * Functions can be grouped ("serverless services") for easy management of code, resources & processes, across large projects & teams. From 778f510c35efcc7969f1464bc49a48ca0016a965 Mon Sep 17 00:00:00 2001 From: Doug Moscrop Date: Mon, 18 Sep 2017 14:43:23 -0400 Subject: [PATCH 53/56] fix #4272 use instanceof to detect duplicate plugins and log a warning --- lib/classes/PluginManager.js | 6 ++++-- lib/classes/PluginManager.test.js | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/lib/classes/PluginManager.js b/lib/classes/PluginManager.js index 5e0b3f7d4..75fd3d89e 100644 --- a/lib/classes/PluginManager.js +++ b/lib/classes/PluginManager.js @@ -67,8 +67,10 @@ class PluginManager { } // don't load plugins twice - const loadedPlugins = this.plugins.map(plugin => plugin.constructor.name); - if (_.includes(loadedPlugins, Plugin.name)) return; + if (this.plugins.some(plugin => plugin instanceof Plugin)) { + this.serverless.cli.log(`WARNING: duplicate plugin ${Plugin.name} was not loaded\n`); + return; + } this.loadCommands(pluginInstance); this.loadHooks(pluginInstance); diff --git a/lib/classes/PluginManager.test.js b/lib/classes/PluginManager.test.js index 9fec8833b..95864844c 100644 --- a/lib/classes/PluginManager.test.js +++ b/lib/classes/PluginManager.test.js @@ -546,6 +546,28 @@ describe('PluginManager', () => { expect(pluginManager.plugins.length).to.equal(1); }); + it('should load two plugins that happen to have the same class name', () => { + function getFirst() { + return class PluginMock { + }; + } + + function getSecond() { + return class PluginMock { + }; + } + + const first = getFirst(); + const second = getSecond(); + + pluginManager.addPlugin(first); + pluginManager.addPlugin(second); + + expect(pluginManager.plugins[0]).to.be.instanceof(first); + expect(pluginManager.plugins[1]).to.be.instanceof(second); + expect(pluginManager.plugins.length).to.equal(2); + }); + it('should load the plugin commands', () => { pluginManager.addPlugin(SynchronousPluginMock); From 3cbea4df9c84a12d80b216a0e1eaa3521ca42e13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Guimar=C3=A3es?= Date: Mon, 18 Sep 2017 18:45:36 -0300 Subject: [PATCH 54/56] - Upgrade deps - Remove invoke comment because webpack is now integrated with sls invoke local - Adding typescript extensions do webpack resolves --- .../create/templates/aws-nodejs-typescript/package.json | 8 ++++---- .../templates/aws-nodejs-typescript/serverless.yml | 3 --- .../templates/aws-nodejs-typescript/webpack.config.js | 9 ++++++++- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/plugins/create/templates/aws-nodejs-typescript/package.json b/lib/plugins/create/templates/aws-nodejs-typescript/package.json index dd30cbaae..ce7044b11 100644 --- a/lib/plugins/create/templates/aws-nodejs-typescript/package.json +++ b/lib/plugins/create/templates/aws-nodejs-typescript/package.json @@ -7,10 +7,10 @@ "test": "echo \"Error: no test specified\" && exit 1" }, "devDependencies": { - "serverless-webpack": "^2.2.0", - "ts-loader": "^2.3.1", - "typescript": "^2.4.2", - "webpack": "^3.3.0" + "serverless-webpack": "^3.0.0", + "ts-loader": "^2.3.7", + "typescript": "^2.5.2", + "webpack": "^3.6.0" }, "author": "The serverless webpack authors (https://github.com/elastic-coders/serverless-webpack)", "license": "MIT" diff --git a/lib/plugins/create/templates/aws-nodejs-typescript/serverless.yml b/lib/plugins/create/templates/aws-nodejs-typescript/serverless.yml index ba0f16154..bb8a2f566 100644 --- a/lib/plugins/create/templates/aws-nodejs-typescript/serverless.yml +++ b/lib/plugins/create/templates/aws-nodejs-typescript/serverless.yml @@ -10,9 +10,6 @@ provider: runtime: nodejs6.10 functions: - # Example with LAMBDA-PROXY integration - # Invoking locally: - # sls webpack invoke -f hello hello: handler: handler.hello events: diff --git a/lib/plugins/create/templates/aws-nodejs-typescript/webpack.config.js b/lib/plugins/create/templates/aws-nodejs-typescript/webpack.config.js index 58534724e..c80f4c986 100644 --- a/lib/plugins/create/templates/aws-nodejs-typescript/webpack.config.js +++ b/lib/plugins/create/templates/aws-nodejs-typescript/webpack.config.js @@ -1,9 +1,16 @@ const path = require('path'); -// eslint-disable-next-line import/no-unresolved const slsw = require('serverless-webpack'); module.exports = { entry: slsw.lib.entries, + resolve: { + extensions: [ + '.js', + '.json', + '.ts', + '.tsx' + ] + }, output: { libraryTarget: 'commonjs', path: path.join(__dirname, '.webpack'), From cd33957793a3bfa0d8c32e6338c3c477c0507bbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Guimar=C3=A3es?= Date: Mon, 18 Sep 2017 19:36:42 -0300 Subject: [PATCH 55/56] Adging jsx --- .../create/templates/aws-nodejs-typescript/webpack.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/plugins/create/templates/aws-nodejs-typescript/webpack.config.js b/lib/plugins/create/templates/aws-nodejs-typescript/webpack.config.js index c80f4c986..b15fcd6d4 100644 --- a/lib/plugins/create/templates/aws-nodejs-typescript/webpack.config.js +++ b/lib/plugins/create/templates/aws-nodejs-typescript/webpack.config.js @@ -6,6 +6,7 @@ module.exports = { resolve: { extensions: [ '.js', + '.jsx', '.json', '.ts', '.tsx' From ae914264e8da02df107dcd731b70d99c1ed65d19 Mon Sep 17 00:00:00 2001 From: David Wells Date: Mon, 18 Sep 2017 16:39:02 -0700 Subject: [PATCH 56/56] Update getting-started.md --- docs/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 4ef15a86f..7ceb0feac 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -23,7 +23,7 @@ npm install -g serverless serverless login ``` -*Serverless follows the [Semantic Versioning](http://semver.org) schema. You can read more about that in our dedicated [versioning file](http://bit.ly/2eP05Iw).* +Next up, it's time to choose where you'd like your serverless service to run. ## Choose your compute provider