diff --git a/README.md b/README.md
index 14f75373f..03f42434d 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)
@@ -129,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.
@@ -152,40 +154,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 +215,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 +238,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 +262,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 +308,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
diff --git a/docker-compose.yml b/docker-compose.yml
index d16161c14..5997745a6 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-nodejs-gradle:
+ image: pgoudreau/docker-maven-node
+ volumes:
+ - ./tmp/serverless-integration-test-aws-kotlin-nodejs-gradle:/app
aws-groovy-gradle:
image: java:8
volumes:
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
diff --git a/docs/providers/aws/cli-reference/create.md b/docs/providers/aws/cli-reference/create.md
index 268ace7ed..e88221851 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-nodejs-gradle
- aws-groovy-gradle
- aws-java-maven
- aws-java-gradle
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
+```
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/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
diff --git a/docs/providers/aws/guide/services.md b/docs/providers/aws/guide/services.md
index d6e18d93c..20a27a027 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-nodejs-gradle
* aws-groovy-gradle
* aws-java-gradle
* aws-java-maven
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
diff --git a/lib/classes/CLI.js b/lib/classes/CLI.js
index 4a186a1a5..cb177428b 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 disable CLI colors'));
this.consoleLog(chalk.dim('* Pass "--help" after any for contextual help'));
this.consoleLog('');
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);
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() {
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', () => {
diff --git a/lib/plugins/aws/deploy/lib/uploadArtifacts.js b/lib/plugins/aws/deploy/lib/uploadArtifacts.js
index 380fcea1e..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);
@@ -73,7 +73,7 @@ module.exports = {
}
return this.provider.request('S3',
- 'putObject',
+ 'upload',
params,
this.options.stage,
this.options.region);
@@ -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);
diff --git a/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js b/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js
index 059495288..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
@@ -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,11 +175,11 @@ 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',
- 'putObject',
+ 'upload',
{
Bucket: awsDeploy.bucketName,
Key: `${awsDeploy.serverless.service.package.artifactDirectoryName}/artifact.zip`,
@@ -207,11 +207,11 @@ 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',
- 'putObject',
+ 'upload',
{
Bucket: awsDeploy.bucketName,
Key: `${awsDeploy.serverless.service.package.artifactDirectoryName}/artifact.zip`,
diff --git a/lib/plugins/aws/invokeLocal/.gitignore b/lib/plugins/aws/invokeLocal/.gitignore
index 0d20b6487..376be9d3e 100644
--- a/lib/plugins/aws/invokeLocal/.gitignore
+++ b/lib/plugins/aws/invokeLocal/.gitignore
@@ -1 +1,5 @@
*.pyc
+java/target
+java/.project
+java/.settings
+java/.classpath
diff --git a/lib/plugins/aws/invokeLocal/index.js b/lib/plugins/aws/invokeLocal/index.js
index 76ad4c4e1..9fafc1c48 100644
--- a/lib/plugins/aws/invokeLocal/index.js
+++ b/lib/plugins/aws/invokeLocal/index.js
@@ -3,6 +3,7 @@
const BbPromise = require('bluebird');
const _ = require('lodash');
const os = require('os');
+const fs = BbPromise.promisifyAll(require('fs'));
const path = require('path');
const validate = require('../lib/validate');
const chalk = require('chalk');
@@ -137,8 +138,17 @@ 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.');
+ .Error('You can only invoke Node.js, Python & Java functions locally.');
}
invokeLocalPython(runtime, handlerPath, handlerName, event, context) {
@@ -163,6 +173,68 @@ 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'),
+ ]);
+
+ 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.`);
+ }));
+ }
+
+ 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: customContext || context,
+ });
+
+ 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(() => {
+ const mvn = spawn('mvn', [
+ 'package',
+ '-f',
+ path.join(javaBridgePath, 'pom.xml'),
+ ]);
+
+ 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));
+ }));
+ }
+
invokeLocalNodeJs(handlerPath, handlerName, event, customContext) {
let lambda;
diff --git a/lib/plugins/aws/invokeLocal/index.test.js b/lib/plugins/aws/invokeLocal/index.test.js
index 4222be2c6..8e73d67ec 100644
--- a/lib/plugins/aws/invokeLocal/index.test.js
+++ b/lib/plugins/aws/invokeLocal/index.test.js
@@ -3,6 +3,9 @@
const expect = require('chai').expect;
const sinon = require('sinon');
const path = require('path');
+const mockRequire = require('mock-require');
+const EventEmitter = require('events');
+const fse = require('fs-extra');
const AwsInvokeLocal = require('./index');
const AwsProvider = require('../provider/awsProvider');
const Serverless = require('../../../Serverless');
@@ -276,12 +279,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 +304,7 @@ describe('AwsInvokeLocal', () => {
afterEach(() => {
awsInvokeLocal.invokeLocalNodeJs.restore();
awsInvokeLocal.invokeLocalPython.restore();
+ awsInvokeLocal.invokeLocalJava.restore();
});
it('should call invokeLocalNodeJs when no runtime is set', () => awsInvokeLocal.invokeLocal()
@@ -352,12 +359,26 @@ describe('AwsInvokeLocal', () => {
{},
undefined
)).to.be.equal(true);
- delete awsInvokeLocal.options.functionObj.runtime;
});
});
- it('throw error when using runtime other than Node.js or Python', () => {
+ it('should call invokeLocalJava when java8 runtime is set', () => {
awsInvokeLocal.options.functionObj.runtime = 'java8';
+ return awsInvokeLocal.invokeLocal()
+ .then(() => {
+ expect(invokeLocalJavaStub.calledOnce).to.be.equal(true);
+ expect(invokeLocalJavaStub.calledWithExactly(
+ 'java',
+ 'handler.hello',
+ undefined,
+ {},
+ undefined
+ )).to.be.equal(true);
+ });
+ });
+
+ it('throw error when using runtime other than Node.js or Python', () => {
+ awsInvokeLocal.options.functionObj.runtime = 'invalid-runtime';
expect(() => awsInvokeLocal.invokeLocal()).to.throw(Error);
delete awsInvokeLocal.options.functionObj.runtime;
});
@@ -507,4 +528,183 @@ 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()', () => {
+ const bridgePath = path.join(__dirname, 'java', 'target');
+ let callJavaBridgeStub;
+
+ beforeEach(() => {
+ fse.mkdirsSync(bridgePath);
+ callJavaBridgeStub = sinon.stub(awsInvokeLocal, 'callJavaBridge').resolves();
+ awsInvokeLocal.options = {
+ stage: 'dev',
+ function: 'first',
+ functionObj: {
+ handler: 'handler.hello',
+ name: 'hello',
+ timeout: 4,
+ },
+ data: {},
+ };
+ });
+
+ afterEach(() => {
+ awsInvokeLocal.callJavaBridge.restore();
+ fse.removeSync(bridgePath);
+ });
+
+ it('should invoke callJavaBridge when bridge is built', () =>
+ 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);
+ })
+ );
+
+ describe('when attempting to build the 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(() => {
+ awsInvokeLocalMocked.callJavaBridge.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(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);
+ })
+ );
+ });
+ });
});
diff --git a/lib/plugins/aws/invokeLocal/java/MANIFEST.mf b/lib/plugins/aws/invokeLocal/java/MANIFEST.mf
new file mode 100644
index 000000000..3cc01de12
--- /dev/null
+++ b/lib/plugins/aws/invokeLocal/java/MANIFEST.mf
@@ -0,0 +1,2 @@
+Manifest-version: 1.0
+Main-Class: com.serverless.InvokeBridge
diff --git a/lib/plugins/aws/invokeLocal/java/pom.xml b/lib/plugins/aws/invokeLocal/java/pom.xml
new file mode 100644
index 000000000..c47be034b
--- /dev/null
+++ b/lib/plugins/aws/invokeLocal/java/pom.xml
@@ -0,0 +1,71 @@
+
+
+ 4.0.0
+
+ com.serverless
+ invoke-bridge
+ 1.0
+
+
+ 1.8
+ 1.8
+
+
+
+
+ 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.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
new file mode 100644
index 000000000..b0c93945c
--- /dev/null
+++ b/lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/Context.java
@@ -0,0 +1,86 @@
+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 String logGroupName;
+ private long endTime;
+
+ Context(String name, String version, String logGroupName, int timeout) {
+ this.name = name;
+ this.version = version;
+ this.logGroupName = logGroupName;
+ this.endTime = System.currentTimeMillis() + (timeout * 1000);
+ }
+
+ public String getAwsRequestId() {
+ return "1234567890";
+ }
+
+ public String getLogGroupName() {
+ return this.logGroupName;
+ }
+
+ public String getLogStreamName() {
+ return "LogStream_" + this.name;
+ }
+
+ public String getFunctionName() {
+ return this.name;
+ }
+
+ public String getFunctionVersion() {
+ return this.version;
+ }
+
+ public String getInvokedFunctionArn() {
+ return "arn:aws:lambda:serverless:" + this.name;
+ }
+
+ public CognitoIdentity getIdentity() {
+ return new CognitoIdentity() {
+ public String getIdentityId() {
+ return "1";
+ }
+
+ public String getIdentityPoolId() {
+ return "1";
+ }
+ };
+ }
+
+ public ClientContext getClientContext() {
+ return new ClientContext() {
+ public Client getClient() {
+ return null;
+ }
+
+ public Map getCustom() {
+ return null;
+ }
+
+ public Map getEnvironment() {
+ return System.getenv();
+ }
+ };
+ }
+
+ public int getRemainingTimeInMillis() {
+ return Math.max(0, (int) (this.endTime - System.currentTimeMillis()));
+ }
+
+ public int getMemoryLimitInMB() {
+ return 1024;
+ }
+
+ public LambdaLogger getLogger() {
+ return System.out::println;
+ }
+}
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
new file mode 100644
index 000000000..ab9629426
--- /dev/null
+++ b/lib/plugins/aws/invokeLocal/java/src/main/java/com/serverless/InvokeBridge.java
@@ -0,0 +1,88 @@
+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.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.HashMap;
+
+public class InvokeBridge {
+ private File artifact;
+ private String className;
+ private Object instance;
+ private Class clazz;
+
+ 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(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", "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, logGroupName, timeout);
+ }
+
+ 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, 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();
+ String inputStr;
+
+ while ((inputStr = streamReader.readLine()) != null) {
+ inputStringBuilder.append(inputStr);
+ }
+
+ return inputStringBuilder.toString();
+ }
+
+ public static void main(String[] args) {
+ new InvokeBridge();
+ }
+}
diff --git a/lib/plugins/create/create.js b/lib/plugins/create/create.js
index d552a1732..0bb965e98 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-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 cf3591dc1..00567485e 100644
--- a/lib/plugins/create/create.test.js
+++ b/lib/plugins/create/create.test.js
@@ -228,6 +228,34 @@ describe('Create', () => {
});
});
+ it('should generate scaffolding for "aws-kotlin-nodejs-gradle" template', () => {
+ process.chdir(tmpDir);
+ create.options.template = 'aws-kotlin-nodejs-gradle';
+
+ 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('package.json');
+ 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-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