アイリッジ開発者ブログ

アイリッジに所属するエンジニアが技術情報を発信していきます。

OpenAPI モックサーバ用テンプレート作ってみた

f:id:iridge-tech:20201022103946p:plain

テクノロジーパートナー本部開発部サーバサイドエンジニアの佐藤です。

社内で新規プロジェクトを開始する際に以下のような問題が発生しがちです。

  • 開発初期にAPIドキュメントが不足する
  • アプリ開発者がサーバー間のAPI開発待ちとなって、自前でモックファイルを用意するのは手間

今回はこれら解消するためにOpenAPIモックサーバをさくっと作れるCDKテンプレートを作成したので紹介します

内容紹介

使用しているサービス

実際の使用手順

  1. 環境構築
    • テンプレートプロジェクトをfork
    • npm install、環境変数の設定
    • ドキュメント(後述)デプロイ用のS3バケット作成
  2. モックAPIを定義したyamlを作成

    • yamlの作成にはStoplightが便利です
    • f:id:iridge-tech:20201021153344p:plainf:id:iridge-tech:20201021153401p:plain
  3. ビルド〜デプロイ

    • npm run build& cdk deploy
  4. 完成!
    • f:id:iridge-tech:20201021153441p:plainf:id:iridge-tech:20201021153504p:plain

作業内容の詳細

実装内容をもう少し細かくご紹介します

モックサーバ

APIを定義したサンプルのyamlを配置してDockerfileを作成します。中身はこれだけ

FROM stoplight/prism

COPY spec/oas.yaml /oas.yaml
CMD ["mock", "/oas.yaml", "-h", "0.0.0.0", "-p", "80"]

インフラ

CDKはaws-cdkのREADMEに書いてある通り cdk init を実行し、生成されたlib/stack.tsにコードを追加していきます(実際のコードから一部変更しています)

// ドキュメントを配置するS3バケット
const s3Website = new s3.Bucket(this, 'CdkOpenapiMockTemplateS3Bucket', {
  bucketName: 'S3バケット名',
  accessControl: s3.BucketAccessControl.PUBLIC_READ,
  removalPolicy: cdk.RemovalPolicy.DESTROY,
  websiteIndexDocument: 'index.html',
});
// モックサーバー構築
const vpc = ec2.Vpc.fromLookup(this, 'Vpc', {vpcId: 'VPC ID'});

// Fargate+ALBの作成
// cpu: 256, memoryLimitMiB: 512で作成されます
// 変更したい場合はFargateTaskDefinitionのプロパティで指定してください
const taskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDefinition');
const mainContainerDefinition = taskDefinition.addContainer('Web', {
  image: ecs.ContainerImage.fromAsset('./prism'),
});
mainContainerDefinition.addPortMappings({containerPort: 80});

const albFargateService = new ecs_patterns.ApplicationLoadBalancedFargateService(this, 'FargateService', {
  vpc,
  assignPublicIp: true,
  taskDefinition: taskDefinition,
  domainName: 'モックサーバ ドメイン名',
});
// ヘルスチェックがサーバーエラー以外通るように設定
albFargateService.targetGroup.configureHealthCheck({healthyHttpCodes: '200-499'});

大変だった / 便利だったポイント

  • snapshot時にmockを使うところがたいへんだった。stackを生成する際にmock化した関数をねじ込む必要がある
  • ecs.ContainerImage.fromAsset() が便利。Dockerfileのビルド、ECRの作成、pushの自動化をすべてやってくれる
  • ApplicationLoadBalancedFargateService は抽象度が高く、だいたい必要なリソースを全部作ってくれる

テスト

  • snapshotテストを追加
    • snapshotテストとは、デグレや意図しないリソース変更・削除からスタックを保護するために予め想定したCFnテンプレートである「スナップショット」を取得しておき、CDKコードが生成するCFnテンプレートとの差異をチェックするテスト
    • テストの作成でトリッキーだったところ
      • スタックが既存のVPCを Lookup する作りになっていたため、そこをモックに差し替える方法の調査に少し時間がかかった。結果的には jest.spyOn() で Lookup メソッドを指定して、mockImplementation() でメソッド丸ごと処理を書き換える方法で解決。ダミーVPCオブジェクトの作成が実行されるタイミングで、Lookup メソッドが呼び出される時にオブジェクト作成が実行されないとうまく動かなかった
test('Create OpenAPI Mock to existing VPC', () => {
  // finegrained test
  // 略
  
  // snapshot test
  const app = new cdk.App();
  const stack = new CdkOpenapiMockTemplate.CdkOpenapiMockTemplateStack(app, 'CdkOpenapiMockTemplateStack', {
    env: {
        account: process.env.CDK_DEFAULT_ACCOUNT,
        region: process.env.CDK_DEFAULT_REGION,
    }
  });
  expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot();
});

デプロイ

gitlab-ciでデプロイします

  • deploy_cdk
deploy_cdk:
    stage: deploy
    image: jsii/superchain
    script:
    - npm i
    - npx cdk deploy --require-approval never
    when: manual
  • deploy_html
    • 生成したドキュメントファイルをS3に配置する
deploy_html:
    stage: deploy
    image: openapitools/openapi-generator-cli:v4.3.1
    script:
    - > 
    docker-entrypoint.sh generate
    -i ./prism/spec/oas.yaml
    -o /tmp/html
    -g html2
    - apk update && apk add python3
    - python3 -m ensurepip
    - pip3 install awscli --ignore-installed six
    - aws s3 cp /tmp/html/ s3://<バケット名>/ --recursive --acl public-read
    when: manual

動作確認

  • API
    • http://ALBのFQDN/yamlで指定したpath/to/api にアクセス
  • Document
    • https://s3のアップロード先 にアクセス

まとめ

このテンプレートはインターフェースは決まったもののまだまだアプリ側に見せるには時間がかかる場合や、対向システムの利用が決まっているもののまだ接続できない場合に役立ちそうです。モック用のファイルを1つずつ配置するような手間が省けます。 またyaml定義からドキュメントも生成されるのでエンジニア以外でも内容が確認しやすくなります。 情報が錯綜しがちな開発現場の一助になれば幸いです。