sitateru tech blog: OpenAPI

sitateru tech blog

シタテルの技術やエンジニアの取り組みを紹介するテックブログです。

ラベル OpenAPI の投稿を表示しています。 すべての投稿を表示
ラベル OpenAPI の投稿を表示しています。 すべての投稿を表示

2018年11月30日金曜日

AWSLambdaとServerlessを使ってみる[第2回]

11月 30, 2018

こんにちは、シタテルエンジニアの工です!

AWSLambdaとServerless第2回に入っていきます!
前回は、AWSLambdaとserverlessについてさらっと確認して
serverlessでServiceをcreateするところまでをやってきました。
AWSLambdaとServerlessを使ってみる[第1回]|sitateru tech blog

今回は実際にSansan OpenAPIを利用して名刺情報を取得してみます 🚀

SansanAPI


公式ドキュメント Sansan Open API

名刺API 名刺Set取得(期間指定)を使ってみます。
SansanAPIを利用するにはAPI Keyが必要です。
取得方法はこちらを参考にしてください。

作る

前回createしたServiceを利用して作っていきます。
現在この2ファイルが作成されている状態です。

  • serverless.yml
    • 各種設定を書いていきます
  • handler.js
    • 処理を書いていきます

axiosインストール

今回は、httpリクエストにaxiosを使用します。

$ npm install axios

インストールされました

.
├── handler.js
├── node_modules
│  ├── axios
├── package-lock.json
└── serverless.yml

serverless.yml

環境変数の設定

lambdaには環境変数を設定することができます。
SansanAPIKeyを環境変数として設定してみます。
serverless.yml

# you can define service wide environment variables here
  environment:
    SANSAN_API_KEY: xxx

環境変数を利用する場合は、このように取得できます。

process.env.SANSAN_API_KEY

handler.js

それでは処理を書いていきます。
lambdaの非同期処理とcallbackについてはこちらを参考にしてください。

名刺情報はお見せできないので、取得した件数を出力してみます。

'use strict';

const axios = require('axios')
axios.defaults.headers.common['X-Sansan-Api-Key'] = process.env.SANSAN_API_KEY
axios.defaults.headers.get['Content-Type'] = 'application/json'

module.exports.hello = async (event, context, callback) => {
  var updatedFrom = "2018-11-26T01:00:00Z"
  var updatedTo = "2018-11-27T21:00:00Z"

  await axios.get(`https://api.sansan.com/v2.0/bizCards?updatedTo=${updatedTo}&range=all&entryStatus=completed`)
      .then(function (response) {
        callback(null, response.data.data.length)
      })
      .catch(function (error) {
        callback(error)
      });
};

deploy

cliでdeployします。
serverlessを使わない場合、serviceを手動でzipにしてawsにdeployする必要があるのでとても面倒です...

$ serverless deploy -v

deployが完了したら情報が出力されます。

...
Serverless: Stack update finished...
Service Information
service: hello-sansan
stage: dev
region: us-east-1
stack: hello-sansan-dev
api keys:
  None
endpoints:
  None
functions:
  hello: hello-sansan-dev-hello

lambdaの関数のところにも表示されています。

環境変数も設定されています。

実行

こちらもcliで実行します。

$ sls invoke -f hello
16

件数が取得できました。

おわりに

今回はlambdaからSansanOpenAPIを利用して名刺情報をゲットしてみました!
lambdaはサーバーのこと気にせずに処理だけ書けば良く、serverlessはcliでごにょごにょできるし、とても便利です!
sitateruではlambdaを使って、SansanとHubSpotの同期を自動化しています。
どこかのタイミング第3回としてserverless-offlineを使用したlambdaの開発についても書こうと思います。

2018年11月2日金曜日

Google Cloud EndpointsとAWS上のAPIサーバをつないでみる

11月 02, 2018

こんにちは、シタテルの茨木です。

シタテルではSCS(Sitateru-Control-System)という生産管理システムをRuby on Railsで実装し、AWSでホスティングしています。

SCS自体もViewを持っているのですが、徐々にAPI化とフロントエンドの分離を進めており、一部のフロントエンドは既にSPAで分離されていたりします。

今後はシステムをAPI仕様ベースで分離&疎結合化していきたいねーという流れで、API-Gateway系のプロダクト、今回は特にGoogle Cloud Endpointsに触ってみました。

やること

既にあるAWS上のOpenAPI仕様で実装されたAPIサーバと、Google Cloud Endpointsを接続して、APIアクセスを管理できるか見てみる。

とりあえずトラフィックが見えるところまでで、認証系とかはやりません。

構成イメージ

API-Server ---- ESP ---- Client App
                 |
                 |
          Cloud Endpoints

ESP(Extensible Service Proxy)

ESPはEndpoints固有の要素で、リバースプロキシとしてAPIリクエストを一次受けし、Cloud Endpointsと連携しつつ、通していいリクエストだけバックエンド(API-Server)に流してくれます

  • API-Serverの手前でESPを動作させるのが必須
  • ESPはdockerで稼働が必要、ESPの稼働プラットフォームは任意
  • ESPを間に噛ませさえすれば、API Serverのプラットフォームも任意

手順

身も蓋もないですが公式ガイドに書いてあるのを読むのが確実です

ESP をローカルまたは別のプラットフォームで実行する

ざっと流れを書いておきます

  • GCPにプロジェクト作る

  • プロジェクトにサービスアカウント作って秘密鍵を払い出す

    • サービスアカウントはESPがendpointにアクセスする際に使われます
  • OpenAPIの定義ファイル(いわゆるswagger)をAPIサーバから持ってくる

    • 今回はgrape-swaggerで生成したものを使いました
    • ガイドだとyamlになってますが、jsonでも読んでくれます
  • APIサーバのドメイン所有権の証明を行う

  • gcloud endpoints services deploy [API定義ファイル]でendpoint構成をデプロイする

    • デプロイが通ると、下図の様にGCPのポータル上で見えるようになります
    • あくまでendpoint構成をデプロイしただけで、ESPと繋いでないので何も起きません
  • ESP用のインスタンスをAWS上に作って、dockerでESPを稼働させる

    • docker導入は一般的な手順なので割愛
    • ESP稼働させるdocker runは下記の様になります(サンプル)
sudo docker run \
    --detach \
    --name="esp" \
    --net="host" \
    --volume=$HOME/esp:/esp \
    --publish=8082 \
    gcr.io/endpoints-release/endpoints-runtime:1 \
    --service=hogehoge.sitateru.com \
    --rollout_strategy=managed \
    --http_port=8082 \
    --backend=https://hogehoge.sitateru.com \
    --service_account_key=/esp/key.json

ざっとオプション説明

--service
endpoint構成のサービス名(≒APIサーバのドメイン名≒swaggerのhostエントリ)と一致させる

--backend
ESPのプロキシ先。APIサーバを指定する

--service_account
サービスアカウントの鍵ファイルを指定する

  • ここまでやるとESP経由でリクエストが通るようになります
    • curl等で試してみましょう
curl --request POST \
    --header "Content-Type:application/json" \
    --header "APIサーバ固有で必要なヘッダ等あれば" \
    --data '{"email":"hogehoge", "password":"fugafuga"}' \
    http://(ESPインスタンスのIP):8082/path/to/api
  • ESP経由でのアクセスは、GCPポータルからログが確認できます

ハマった点

上記手順ではすんなりswaggerを読ませていますが、実際は結構エラーと戦いました。

ちゃんと読むとガイドにも書いてありますが、endpoint構成はOpenAPI仕様を完全にはサポートしていないようです。

例1. type: fileはダメっぽい

"/definitions/putApiV2Topics/properties/files/items/type": domain: validation; keyword: enum; message: value not found in enum; enum: ["array","boolean","integer","null","number","object","string"]; value: "file"

type: fileはサポートしてない。
仕様的にはstringに置換してよさそう?

例2. request-paramの渡し方に制限がある

 ERROR: unknown location: http: body field path 'ids' must be a non-repeated message.
 ERROR: unknown location: http: body field path 'api_v2_files' must be a non-repeated message.
 ERROR: unknown location: http: body field path 'api_v2_items_id_files' must be a non-repeated message.
 ERROR: unknown location: http: body field path 'emails' must be a non-repeated message.'

request-parameterのbody直下にarrayはサポートしない模様。
API側のパラメタ受け取り仕様で、arrayはobjectでくるむように作る必要がある?

まとめ

ESPまわりの構成は実際に組むまでイメージ湧かなかったので、その辺の助けになれば幸いです。

OpenAPIはほぼデファクトだと思っていたので、制約がいくつか出てきたのは意外でした。gRPC推しなんですかね?