Serverlessで1サービス：多ファンクション管理

・筆者なりの方法です。メジャーなやり方ではないかもしれません。
・serverless@1.2.1時点でのメモ
・Lambdaプロキシ統合を使用(LAMBDA_PROXY, Lambda Proxy)

# まずサービス作成
```
mkdir サービス名 && cd サービス名
sls create -t aws-nodejs
```

この時点での構成はきっとこんな感じ

|階層1|
|:--|
|./|
|event.json|
|handler.js|
|serverless.yml|

APIGateway + Lambdaでいう「サービス」が
一体どれのことを言っているのかまでは調べきれていないが、
ここではAPIGatewayでの1APIプロジェクトとする。

# 関数準備
## 考える事
・LambdaはWebコンソールで関数の修正が可能。
→1ファイルに複数の関数を入れると、なんだか気持ち悪いことになりそう
→「1LambdaFunction = 1ファイル」がいいはず！

ということで、
こんな構成にする

|階層1|階層2|
|:--|:--|
|./||
|event.json||
|handler.js||
|serverless.yml||
|function_dir1||
|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;L|get.js|
||event_get.json|
|function_dir2||
|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;L|post.js|
||event_post.json|

## 関数ファイルを配備してくれるシェルを準備した
下記をサービス直下(serverless.ymlと同じ位置)に置いて実行してください。
※コードがしょぼいのは気にしないでください

```lang:function.sh
#!/bin/sh

errMsg1="Error：第２引数にはHTTPメソッドが必要です(例：get | post)"

myDir=$(cd $(dirname $0); pwd)
ymlPath=${myDir}"/serverless.yml"

# チェック
## serverless.ymlがある場所を起点とする
if [ ! -e ${ymlPath} ]
then
  echo "Error：サービスディレクトリ直下に配置してください"
  exit
fi

if [ -z ${1} ]
then
  echo "Error：第１引数には作成するディレクトリ名が必要です"
  exit
fi

fDir=${myDir}"/"${1}
fdFlg="true"

if [ -e ${fDir} ]
then
  if [ ! -d ${fDir} ]
  then
    echo "Error：作成予定のファイルが既に存在しており、ディレクトリでないようです"
    exit
  fi

  # ディレクトリ作成フラグ
  fdFlg="false"
fi

if [ -z ${2} ]
then
  echo ${errMsg1}
  exit
fi

if [ "get" != ${2} -a "post" != ${2} ]
then
  echo ${errMsg1}
  exit
fi

cFile=${fDir}"/"${2}".js"

if [ -e ${cFile} ]
then
  echo "Error：関数ファイルが既に存在しています"
  exit
fi

# 処理開始
## ディレクトリ作成
if [ "true" = ${fdFlg} ]
then
  mkdir -m 777 ${fDir}
fi

## 関数ファイル作成
fName="";
if [ "get" = ${2} ]
then
  fName=${1}"Get"
elif [ "post" = ${2} ]
then
  fName=${1}"Post"
fi

touch ${cFile}
chmod 777 ${cFile}

cat <<SETUP > ${cFile}
'use strict';

//External Libraries
//const hoge = require("hoge");

module.exports.${fName} = (event, context, callback) => {
  //Respomse Template
  var response = {
    statusCode: 200,
    headers: {"Access-Control-Allow-Origin" : "*"},
    body: null
  };

  //Response.Body Template
  var body = {
    message: 'successfully!',
    input: event,
    results: null
  };

  try {
    var results = '';

    body.results = results;
    response.body = JSON.stringify(body);
    callback(null, response);
  } catch(e) {
    //Exception
    response.statusCode = 500;
    body.message = e.message;
    response.body = JSON.stringify(body);

    callback(null, response);
  } finally {

  }
};
SETUP

# event.jsonも追加
eFile=${fDir}"/event_"${2}".json"
touch ${eFile}
chmod 777 ${eFile}

methodUpper=$(echo ${2} | tr [a-z] [A-Z])

cat <<SETUP > ${eFile}
{
"resource": "/${1}",
"path": "/${1}",
"httpMethod": "${methodUpper}",
"headers": null,
"queryStringParameters": null,
"pathParameters": null,
"stageVariables": null,
"requestContext": {
  "accountId": "xxxxxxxxxxxx",
  "resourceId": "xxxxxx",
  "stage": "test",
  "requestId": "xxxxxxxxxxxxxxxxxxx",
  "identity": {
    "cognitoIdentityPoolId": null,
    "accountId": "xxxxxxxxxxxx",
    "cognitoIdentityId": null,
    "caller": "xxxxxxxxxxxx",
    "apiKey": "xxxxxxxxxxxxxxxxxxx",
    "sourceIp": "xxxxxxxxxxxxxxxxxxxxx",
    "accessKey": "xxxxxxxxxxxxxxxxxxxx",
    "cognitoAuthenticationType": null,
    "cognitoAuthenticationProvider": null,
    "userArn": "arn:aws:iam::xxxxxxxxxxxx:xxxx",
    "userAgent": "Apache-HttpClient/4.5.x (Java/1.8.0_102)",
    "user": "xxxxxxxxxxxx"
  },
  "resourcePath": "/${1}",
  "httpMethod": "${methodUpper}",
  "apiId": "xxxxxxxxxx"
},
"body": null,
"isBase64Encoded": false
}
SETUP



# serverless.ymlに設定追加
cat <<SETUP >> ${ymlPath}
  ${1}-${2}:
    handler: ${1}/${2}.${fName}
    memorySize: 128
    timeout: 10
    events:
      - http:
          path: ${1}
          method: ${2}
SETUP
```

## 関数配備シェルを実行しよう
```lang:実行方法
sh function.sh 関数ディレクトリ HTTPメソッド
```

```lang:例
sh function.sh list get
```

上記例を実行すると
構成は以下になる。

|階層1|階層2|
|:--|:--|
|./||
|event.json||
|handler.js||
|serverless.yml||
|function.sh||
|list||
|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;L|get.js|
||event_get.json|

serverless.ymlにも設定が追加されているので、
すぐデプロイ出来る状態になります。
※ymlの構成までは見てないので、適時インデントなどは直してください

# デプロイしよう
```
# 一度もデプロイされたことのないものは、単一でアップできないため、
# 初回は必ずサービスまるごとデプロイ
sls deploy

# 一度でもデプロイされていれば単一アップ可
# ※関数名はserverless.ymlで記載したもの
sls deploy function -f 関数名※
```

# 最後に
上記で作成すると次のようなルールになります。

||説明|
|:--|:--|
|サービス名|serverless.ymlに設定したもの|
|パス|関数ディレクトリと同じ|
|HTTPメソッド|function.shの第２引数で指定したもの|
|LambdaFunction名|サービス名-ステージ-パス-HTTPメソッド|
|js関数名|パス+HTTPメソッド<br>例：listGet、indexPost|
|serverless.ymlの関数名|パス-HTTPメソッド|