npm package: @serverless/serverless-components-a
Description: PoC for the next version of the Serverless Framework
Author: Philipp Muens
Latest version: 0.1.2
Deprecated: this prototype has been deprecated. Please checkout serverless-components instead.

# Serverless Components A

## Overview

This project is a prototype of a new concept Serverless has been exploring called "components". Our aim is to introduce highly configurable and composable pieces that allow for multi-cloud & third party resource use cases.

Components are capable of provisioning infrastructure while including both application logic AND lifecycle management. They have a focus on serverless/cloud resources and they also greatly enable reuse, sharing and simplicity.

## Table of contents

* [Getting started](#getting-started)
* [Concepts](#concepts)
  * [Components](#components)
  * [Serverless Registry](#serverless-registry)
* [Docs](#docs)
  * [aws-api-gateway-lambda-endpoint](#aws-api-gateway-lambda-endpoint)
  * [aws-dynamodb-table](#aws-dynamodb-table)
  * [aws-iam-role](#aws-iam-role)
  * [aws-lambda-function](#aws-lambda-function)
  * [github-webhook-receiver](#github-webhook-receiver)
  * [util-aws-request](#util-aws-request)
  * [util-zipper](#util-zipper)

## Getting started

Please make sure that you've installed Node.js with a recent npm version.

**Note:** If not explicitly provided, the prototype will use the `default` AWS profile on your machine.

1. Setup
   1. `npm install --global @serverless/serverless-components-a`
   1. Clone this repository
   1. (Optionally) set your AWS credentials via env variables
      * `export AWS_ACCESS_KEY_ID=my_access_key_id`
      * `export AWS_SECRET_ACCESS_KEY=my_secret_access_key`
1. Configuring
   1. `cd src/examples/github-webhook`
   1. Open up the example in your favorite editor (e.g. `atom .`)
   1. Update the `repository` and `githubToken` parameters with your values
1. Deployment
   1. `proto deploy`
1. Testing
   1. `proto test`
1. Updating
   1. Change some parameters (e.g. the `event` or `httpPath` property)
   1. `proto deploy`
1. Removal
   1. `proto remove`

## Concepts

### Components

Components are a way to share and re-use pre-built functionality throughout different projects. A project can contain one or more components. Furthermore components can be wrapped and composed together to extend functionality.

Components take parameters as their input and compute outputs when run. One example use-case for an output is the `.state.json` file which is created for every project and holds state information for every single component used in the project.

Components can be defined in a `serverless.yml` file like this:

```yml
# serverless.yml
components:
  myDynamoDbTable:
    type: pmuens:aws-dynamodb-table@0.1.0
    parameters:
      attributeDefinitions:
        - attributeName: id
          attributeType: S
      keySchema:
        - attributeName: id
          keyType: HASH
      provisionedThroughput:
        readCapacityUnits: 1
        writeCapacityUnits: 1
      tableName: my-dynamodb-table
```

In this example we're using the `aws-dynamodb-table` component at version `0.1.0` which was authored by `pmuens`. We pass in different parameters to configure the infrastructure this component will setup. In our case we set the `attributeDefinitions`, the `keySchame`, the `provisionedThroughput` as well as the `tableName`.

Most components ship with sane defaults so that it's not necessary to provide all parameters.

A components underpinning functionality is provided by a function.

In this PoC implementation a function is equal to a component. This makes it possible to use components in a declarative way as well as through code.

The function which expresses the components functionality above looks like this:

```javascript
// index.js
module.exports = (context, params, options) => {
  // destructuring the component parameters
  const {
    attributeDefinitions,
    keySchema,
    provisionedThroughput,
    tableName
  } = params

  // ...snip...

  async function createTable(tableName) {
    return sdk('DynamoDB', 'createTable', {
      TableName: tableName
    })
  }

  async function deleteTable(tableName) {
    return sdk('DynamoDB', 'deleteTable', {
      TableName: tableName
    })
  }

  // ...snip...

  // logic to setup the infrastructure
  async function deploy() {
    await createTable(params.tableName)
  }

  // logic to tear down the infrastructure
  async function remove() {
    await deleteTable(params.tableName)
  }

  // these functions are exposed and accessible through the CLI
  return {
    deploy,
    remove
  }
}
```

The components parameters and CLI options are passed in to the function and are accessible via `params` and `options`. The returned functions at the bottom of the function are exposed and accessible via CLI commands.

Treating a component and the corresponding function equally makes it possible to pull in pre-written components in code and therefore re-use them to write parent components which orchestrate different child components. Take a look at the code for the [`github-webhook-receiver`](./src/serverless-registry/pmuens:github-webhook-receiver@0.1.0/index.js) component to see how existing components can be re-used in code.

### Serverless Registry

The ["Serverless Registry"](./src/serverless-registry) is a core part in this implementation since it makes it possible to discover, publish and share existing components.

The registry is not only constrained to serve components. Since components are functions it's possible to wrap existing business logic into functions and publish them to the registry as well.

While implementing this prototype we've created two util functions / components called [`util-aws-request`](./src/serverless-registry/pmuens:util-aws-request@0.1.0/index.js) and [`util-zipper`](./src/serverless-registry/pmuens:util-zipper@0.1.0/index.js). Both are used in various components to steamline AWS SDK calls and `.zip` archive creation.

Looking into the future it could be possible to serve functions which are written in different languages through the registry.

## Docs

### `aws-api-gateway-lambda-endpoint`

Creates / Removes an API endpoint which is exposed via AWS API Gateway and connects directly to an AWS Lambda function.

#### Parameters

| Name        | Description                                                                                         | Type   |
| ----------- | --------------------------------------------------------------------------------------------------- | ------ |
| `path`      | The HTTP path                                                                                       | String |
| `method`    | The HTTP method                                                                                     | String |
| `lambdaArn` | The AWS Lambda functions `arn` which should be connected via the API Gateway                        | String |
| `roleArn`   | The role `arn` which is used to give the API Gateway the permission to call the AWS Lambda function | String |

#### Commands

* `proto deploy`
* `proto remove`

#### Example

```yml
type: pmuens:aws-api-gateway-lambda-endpoint@0.1.0
parameters:
  path: my-path
  method: GET
  lambdaArn: arn:aws:lambda:us-east-1:XXXXX:function:some-lambda-function
  roleArn: arn:aws:iam::XXXXX:role/some-api-gateway-role
```

### `aws-dynamodb-table`

Creates / Removes an AWS DynamoDB table.

#### Parameters

| Name                    | Description                       | Type   |
| ----------------------- | --------------------------------- | ------ |
| `attributeDefinitions`  | The tables attribute definitions  | Array  |
| `keySchema`             | The tables key schema             | Array  |
| `provisionedThroughput` | The tables provisioned throughput | Object |
| `tableName`             | The tables name                   | String |

#### Commands

* `proto deploy`
* `proto remove`

#### Example

```yml
type: pmuens:aws-dynamodb-table@0.1.0
parameters:
  attributeDefinitions:
    - attributeName: id
      attributeType: S
  keySchema:
    - attributeName: id
      keyType: HASH
  provisionedThroughput:
    readCapacityUnits: 1
    writeCapacityUnits: 1
  tableName: my-dynamodb-table
```

### `aws-iam-role`

Creates / Removes an AWS IAM role.

#### Parameters

| Name        | Description                              | Type   |
| ----------- | ---------------------------------------- | ------ |
| `roleName`  | The name for the IAM role                | String |
| `service`   | The service the role targets             | String |
| `policyArn` | The policy `arn` this role should assume | String |

#### Commands

* `proto deploy`
* `proto remove`

#### Example

```yml
type: pmuens:aws-iam-role@0.1.0
parameters:
  roleName: my-ec2-role
  service: ec2.amazonaws.com
  policyArn: arn:aws:iam::aws:policy/AdministratorAccess
```

### `aws-lambda-function`

Creates / Removes an AWS Lambda function.

#### Parameters

| Name           | Description                                              | Type   |
| -------------- | -------------------------------------------------------- | ------ |
| `codeDirPath`  | Path to the location where the functions code is located | String |
| `functionName` | The functions name                                       | String |
| `handler`      | The functions handler                                    | String |
| `roleArn`      | The role `arn` the lambda should use                     | String |
| `runtime`      | The functions runtime                                    | String |
| `environment`  | The functions environment variables                      | Object |
| `memorySize`   | The functions memory size                                | Number |
| `timeout`      | The functions timeout                                    | Number |

#### Commands

* `proto deploy`
* `proto remove`

#### Example

```yml
type: pmuens:aws-lambda-function@0.1.0
parameters:
  codeDirPath: ./code
  functionName: my-lambda-function
  handler: index.handler
  roleArn: arn:aws:iam::XXXXX:role/some-lambda-role
  runtime: nodejs6.10
  environment:
    variables:
      foo: bar
  memorySize: 512
  timeout: 60
```

### `github-webhook-receiver`

Creates / Removes / Tests a GitHub Webhook. Uses AWS as the cloud provider.

AWS resource names created by this component:

| Resource              | Name                       |
| --------------------- | -------------------------- |
| Lambda function name  | `${name}-lambda-function`  |
| DynamoDB table name   | `${name}-logging-table`    |
| Lambda role name      | `${name}-role-lambda`      |
| API Gateway role name | `${name}-role-api-gateway` |

#### Parameters

| Name          | Description                                                                                      | Type   |
| ------------- | ------------------------------------------------------------------------------------------------ | ------ |
| `name`        | The webhooks name (_Note:_ this name will be used as a prefix for all AWS resources (see above)) | String |
| `codeDirPath` | Path to the location where the functions code is located                                         | String |
| `httpPath`    | The HTTP path                                                                                    | String |
| `repository`  | Name of the GitHub repository the Webhook should be created for                                  | String |
| `event`       | The Webhooks event                                                                               | String |
| `githubToken` | The GitHub token which is used to create the Webhook on your behalf                              | String |

#### Commands

* `proto deploy`
* `proto remove`
* `proto test`

#### Example

```yml
type: pmuens:github-webhook-receiver@0.1.0
parameters:
  name: my-github-webhook
  codeDirPath: ./code
  httpPath: webhook
  repository: jdoe/some-repo
  event: issue_comment
  githubToken: s0me3g1thu8t0k3n
```

### `util-aws-request`

Helper which wraps the AWS SDK and provides some convenience functions.

#### Parameters

* None

#### Commands

* None

#### Example

```javascript
const request = loadFunction(context, 'pmuens:util-aws-request@0.1.0', params, options)

const res = await request.send('Lambda', 'deleteFunction', {
  FunctionName: params.functionName
})
```

### `util-zipper`

Helper which provides functionality to create a `.zip` archive.

#### Parameters

* None

#### Commands

* None

#### Example

```javascript
const zipper = loadFunction(context, 'pmuens:util-zipper@0.1.0', params, options)

const artifactFilePath = await zipper.zip(codeDirPath, context.componentRootPath)
```