テクノロジーパートナー本部開発部サーバサイドエンジニアの佐藤です。
社内で新規プロジェクトを開始する際に以下のような問題が発生しがちです。
- 開発初期にAPIドキュメントが不足する
- アプリ開発者がサーバー間のAPI開発待ちとなって、自前でモックファイルを用意するのは手間
今回はこれら解消するためにOpenAPIモックサーバをさくっと作れるCDKテンプレートを作成したので紹介します
内容紹介
使用しているサービス
- aws-cdk 1.48
- stoplight/prism
- jest 25.5
- typescript 3.7.2
実際の使用手順
- 環境構築
- テンプレートプロジェクトをfork
npm install
、環境変数の設定- ドキュメント(後述)デプロイ用のS3バケット作成
モックAPIを定義したyamlを作成
- yamlの作成にはStoplightが便利です
ビルド〜デプロイ
npm run build
&cdk deploy
- 完成!
作業内容の詳細
実装内容をもう少し細かくご紹介します
モックサーバ
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 メソッドが呼び出される時にオブジェクト作成が実行されないとうまく動かなかった
- スタックが既存の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定義からドキュメントも生成されるのでエンジニア以外でも内容が確認しやすくなります。 情報が錯綜しがちな開発現場の一助になれば幸いです。