Upgrade to Pro — share decks privately, control downloads, hide ads and more …

OpenAPIで 楽に始めるスキーマ駆動開発実践論 - PHP Conference 2022

taki komiyama
September 21, 2022

OpenAPIで 楽に始めるスキーマ駆動開発実践論 - PHP Conference 2022

スキーマ駆動開発してますか?

一応OpenAPI(Swagger)書いてるけど、実装と乖離して放置されてませか?

レスポンスクラスなどをきれいに書こうとしたものの、データクラスが増えて面倒になっていませんか?

これまで2年以上開発してきた経験から、スキーマ駆動開発の勘所をご紹介します。

正しくOpenAPIを書いて、OpenAPI GeneratorやPostmanを使いこなせば、リクエスト&レスポンスのデータクラスやAPIクライアント、テストコードが自動生成できます。

最大限OpenAPIを使い倒して、楽に効率化することを目指します。もちろんスキーマと実装の乖離は絶対におきず、複雑な設定ファイルは必要ありません。

PHPを前提に話しますが、多くの部分は他の言語で応用可能です。結合テストの実行で少しDI(Dependency Injection)が出てきますが、DIの考え方など基礎的な内容に触れません。

■話すこと
・スキーマ駆動開発とはなにか
・スキーマ駆動開発のメリットとデメリット
・OpenAPIとは
・OpenAPI GeneratorでAPIクライアントの自動生成
・スキーマと実装をずれないようにする
・Postman(Newman)で結合テストの自動生成
・DIで副作用を防ぐ

■話さないこと
・OpenAPIの詳しい書き方
・DIの考え方
・DIのライブラリについて
・npmについて

■想定対象者
・スキーマ駆動開発をやってみたい人
・スキーマ駆動開発をしているが、実装がスキーマとズレて困っている人
・APIの結合テストを自動作成したい人

taki komiyama

September 21, 2022
Tweet

More Decks by taki komiyama

Other Decks in Programming

Transcript

  1. Bengo4.com, Inc. OpenAPIͰָʹ࢝ΊΔ εΩʔϚۦಈ։ൃ࣮ફ࿦ খٶࢁ ଠथ

  2. Bengo4.com, Inc. หޢ࢜υοτίϜגࣜձࣾ ࡏ੶4೥൒ PHPྺ 6೥ TypeScriptྺ 2೥ ίϯςφΞϓϦ։ൃ&ӡ༻͕ಘҙ খٶࢁ

    ଠथʢ͜Έ΍· ͖ͨʣ 2 ࣗݾ঺հ
  3. Bengo4.com, Inc. 1. εΩʔϚۦಈ։ൃ ◦ OpenAPIͱ͸ ◦ ։ൃͷྲྀΕ ◦ ϝϦοτɾσϝϦοτ

    2. OpenAPI GeneratorͰAPIͱΫϥΠΞϯτΛܨ͙ 3. OAS͔ΒAPIςετΛࣗಈੜ੒ ໨࣍ 3 ·ͣεΩʔϚۦಈ։ൃͷ֓ ཁΛ೺Ѳͨ͠ޙɺ۩ମత ͳ࣮ફํ๏ʹೖΓ·͢ɻ
  4. εΩʔϚۦಈ։ൃ 4

  5. Bengo4.com, Inc. 5 Answer • ݫີͳఆٛ͸·ͩͳ͍ • HTTP APIΛهࡌͨ͠࢓༷ ◦

    URLɺϝιου ◦ ϦΫΤετɺϨεϙϯε Question ࠷ۙʮεΩʔϚۦಈ։ൃʯΛΑ͘ฉ͖·͢ɻεΩʔϚ͸ Ͳ͏͍͏ҙຯͰ͔͢ʁ
  6. Bengo4.com, Inc. • Restful HTTP APIΛఆٛ ͢Δۀքඪ४ͷ࢓༷ • yaml /

    jsonΛ࢖͍Open API Specification(OAS)Ͱ ఆٛ OpenAPIͱSwagger 6 • ࢓༷Λ࣮૷͢ΔͨΊͷπʔ ϧ܈ • yaml / jsonΛ࢖͍Swagger SpecificationͰఆٛ
  7. Bengo4.com, Inc. OpenAPI Specification(OAS) 7 /pets/{petId}: get: operationId: showPetById tag:

    - pets parameters: - name: petId in: path required: true schema: type: integer openapi: "3.0.0" info: version: 1.0.0 title: Swagger Petstore license: name: MIT servers: - url: http://petstore.swagger.io/v1 paths: /pets/{petId}: get:
  8. Bengo4.com, Inc. • Generate UI • Visual Editor • Mock

    Server • Codegen OpenAPIͷΤίγεςϜ 8
  9. Bengo4.com, Inc. OpenAPI.Tools 9

  10. Bengo4.com, Inc. 10 Answer • v3.0͕͓͢͢Ί • v3.1͸େ͖͍มߋ͕͋Δ ◦ ηϚϯςΟοΫόʔδϣχϯά

    • ΤίγεςϜ͕v3.1ʹ௥͍͍ͭͯͳ͍ ◦ OpenAPI Generator΋ະରԠ Question OpenAPI͸࠷৽ͷv3.1ʢ2021-02-15 ϦϦʔεʣ͔v3.0 ͲͪΒΛ࢖͏΂͖Ͱ͔͢ʁ
  11. Bengo4.com, Inc. 1. ίʔυϑΝʔετ ◦ ίʔυ͔ΒOpenAPI SpecΛੜ੒ 2. εΩʔϚϑΝʔετ ◦

    OpenAPI Spec͔ΒίʔυΛੜ੒ OpenAPIΛ࠾༻͢Δ࣌ͷ2ͭͷબ୒ࢶ 11
  12. ࠓճ࿩͢ͷ͸ɺ εΩʔϚϑΝʔετ 12

  13. Bengo4.com, Inc. • ίʔυΛࣗಈੜ੒͢Δ࢓૊Έ͕͋Δ ◦ OpenAPI GeneratorͳͲ • ίΞϩδοΫͷ։ൃʹूதͰ͖ͯɺϦϦʔεߴ଎Խ •

    OAS͸ͨͩͷ࢓༷ॻͰ͸ͳ͍ ͳͥεΩʔϚϑΝʔετ͔ 13
  14. Bengo4.com, Inc. 1. APIͷURL΍Ϩεϙϯεͷ࢓༷ΛܾΊΔ 2. OASͰyamlΛఆٛ͢Δ 3. OAS͔ΒAPIΫϥΠΞϯτͱςετΛࣗಈੜ੒ 4. APIΛ࣮૷͢Δ

    5. ςετΛ࣮ߦ͢Δ εΩʔϚϑΝʔετͷ։ൃͷྲྀΕ 14
  15. Bengo4.com, Inc. • APIΫϥΠΞϯτͳͲίʔυΛࣗಈੜ੒Ͱ͖Δ • API ϞοΫαʔόʔΛཱͯΒΕΔ ◦ API ͕ͳͯ͘΋ϑϩϯτΤϯυͷ։ൃ͕Ͱ͖Δ

    • ࢓༷͕Θ͔Γ΍͍͢ ϝϦοτ 15
  16. Bengo4.com, Inc. • OASͷॻ͖ํΛ֮͑Δඞཁ͋Γ ◦ OASͱੜ੒͞ΕΔίʔυͷରԠ • OpenAPI Generator ◦

    ग़ྗݴޠ͝ͱʹରԠ͢Δػೳ͕ҟͳΔ ◦ ྫɿOneOfͳͲੜ੒Ͱ͖ͳ͍هड़͕͋Δ σϝϦοτ 16
  17. Bengo4.com, Inc. 17 Answer • OAS͔ΒԼهΛࣗಈੜ੒͠·͠ΐ͏ ◦ APIΫϥΠΞϯτ ◦ APIςετ

    • CIͰAPIςετΛ࣮ߦ • ဃ཭͸ݫېͰ͢ɻ Question OASͱ࣮૷͕ဃ཭͍ͯ͠·͢ɻɻͲ͏͢Ε͹࣮૷ͱOAS ͕ҰॹʹͳΓ·͔͢ʁ
  18. OASΛ࠷େݶ׆༻ͯ͠ ίʔυΛࣗಈੜ੒͢Δͷ͕ εΩʔϚۦಈ։ൃ 18

  19. OpenAPI Generator ͰAPIͱΫϥΠΞϯτΛܨ͙ 19

  20. Bengo4.com, Inc. • OAS͔Βίʔυ࡞੒͢Δπʔϧ ◦ APIΫϥΠΞϯτ& Mock Server ◦ ରԠݴޠ/Framework

    100Ҏ্ • Docker ίϯςφ͋Γ • ੲSwagger Codegen͔ΒϑΥʔΫͨ͠ • ରԠݴޠ΋ଟ͘ɺDoc΋ஸೡ OpenAPI Generatorͱ͸ 20
  21. Bengo4.com, Inc. • ໊લۭؒ͸σϑΥϧτͰࢦఆՄೳ • templateΛ࢖͑͹ɺࣗ෼ͰclassΛఆٛͰ͖Δ APIΫϥΠΞϯτΛࣗಈੜ੒ 21 $ openapi-generator-cli

    generate \ --additional-properties=invokerPackage=Bengo4\\API\\Sdk, \ variableNamingConvention=camelCase \ -t ./templates/php \ -i openapi.yaml -g php -o ./api/app/sdk
  22. Bengo4.com, Inc. • APIΫϥΠΞϯτ ◦ Guzzle Http • σʔλϞσϧ •

    ۭͷPHPUnitͷςετ • υΩϡϝϯτ PHP: ੜ੒͞ΕΔίʔυ 22 . ├── README.md ├── composer.json ├── docs ├── lib │ ├── Api │ │ └── PetsApi.php │ ├── ApiException.php │ ├── Configuration.php │ ├── HeaderSelector.php │ ├── Model │ │ ├── Error.php │ │ ├── ModelInterface.php │ │ └── Pet.php │ └── ObjectSerializer.php ├── phpunit.xml.dist └── test ├── Api │ └── PetsApiTest.php └── Model ├── ErrorTest.php └── PetTest.php
  23. Bengo4.com, Inc. PHP: Data Model class -> __construct 23 class

    Pet implements ModelInterface, ArrayAccess, \JsonSerializable { public static function setters() public static function getters() public function __construct(array $data = null) public function listInvalidProperties() public function valid() public function getId() public function setId($id) . ├── README.md ├── composer.json ├── docs ├── lib │ ├── Api │ ├── ApiException.php │ ├── Configuration.php │ ├── HeaderSelector.php │ ├── Model │ │ ├── Error.php │ │ ├── ModelInterface.php │ │ └── Pet.php │ └── ObjectSerializer.php ├── phpunit.xml.dist └── test
  24. Bengo4.com, Inc. PHP: Data Model class -> __construct 24 /**

    * Constructor * * @param mixed[] $data Associated array of property values * initializing the model */ public function __construct(array $data = null) { $this->container['id'] = $data['id'] ?? null; $this->container['name'] = $data['name'] ?? null; $this->container['tag'] = $data['tag'] ?? null; }
  25. Bengo4.com, Inc. PHP: Data Model class -> validation 25 public

    function listInvalidProperties() { $invalidProperties = []; if ($this->container['id'] === null) { $invalidProperties[] = "'id' can't be null"; } if ($this->container['name'] === null) { $invalidProperties[] = "'name' can't be null"; } return $invalidProperties; }
  26. Bengo4.com, Inc. PHP: PHPUnit Test 26 use PHPUnit\Framework\TestCase; class PetTest

    extends TestCase { /** * Test "Pet" */ public function testPet() { // TODO: implement $this->markTestIncomplete('Not implemented'); } . ├── README.md ├── composer.json ├── docs ├── lib ├── phpunit.xml.dist └── test ├── Api │ └── PetsApiTest.php └── Model ├── ErrorTest.php └── PetTest.php
  27. Bengo4.com, Inc. • هड़͢΂͖ϓϩύςΟ ◦ operationId(ؔ਺໊) ◦ tags(Ϋϥε໊) ◦ readonly

    ◦ required ◦ format(ਖ਼نදݱ) ៉ྷʹग़ྗ͞ΕΔOASͷॻ͖ํ 27 /pets/{petId}: get: operationId: showPetById tag: - pets parameters: - name: petId in: path required: true schema: type: integer
  28. 1. validateίϚϯυͰOASΛकΔ 2. ࣗ෼ͷAPI ΫϥΠΞϯτͰϦΫΤετ&Ϩεϙϯ εΛറΔ OpenAPI GeneratorΛ࢖͏ 2ͭͷϙΠϯτ 28

  29. Bengo4.com, Inc. • OASΛνΣοΫ͢ΔvalidateίϚϯυ • generateίϚϯυͷલʹ࣮ߦඞਢ ϙΠϯτ1.validateίϚϯυͰOASΛकΔ 29 $ openapi-generator-cli

    validate -i openapi.yaml Validating spec (openapi.yml) Errors: - attribute components.schemas.Response.desc is unexpected [error] Spec has 1 errors.
  30. Bengo4.com, Inc. • APIΫϥΠΞϯτͷதʹ σʔλΫϥε͕ग़ྗ • σʔλΫϥεΛ௨͢ ◦ ϦΫΤετ ◦

    Ϩεϙϯε • ܕʹΑΔԸܙ ϙΠϯτ2.ࣗ෼ͷAPIΫϥΠΞϯτͰࣗ෼ΛറΔ 30 use Bengo4\App\Sdk\Model\Pet; class PetController { public function get() { $response = new Pet([ 'id' => 1, 'name' => 'Doggie', ]); $this->withJson($response); } }
  31. OAS͔Β APIςετΛࣗಈੜ੒͢Δ 31

  32. Bengo4.com, Inc. ۜͷ஄ؙ͸ͳ͍ͷͰɺཁ݅ʹ߹ΘͤͯબͿ • OpenAPI Generator • Postman • Dredd

    APIςετΛࣗಈੜ੒͢Δબ୒ࢶ 32 ࣗ༝౓& ෳࡶੑ
  33. Bengo4.com, Inc. • JavaͷMustacheͰهड़ͨ͠templateΛ࢖͏ • ࣗ༝ͳهड़͕Մೳ͕ͩɺෳࡶʹͳΔ OpenAPI GeneratorͰAPI Test 33

    /** * Test case for {{{operationId}}} * * {{{summary}}}. */ public function test{{vendorExtensions.x-test-operation-id}}()
  34. Bengo4.com, Inc. • APIΛ։ൃ͢ΔͨΊͷϓϥοτϑΥʔϜ ◦ APIΫϥΠΞϯτɺςετɺυΩϡϝϯτͷੜ੒ • Newmanͱͯ͠CLI΋ఏڙ • OpenAPIΛPostmanͷJsonʹม׵Մೳ

    ◦ ࣗಈͰςετΞαʔγϣϯ͸௥Ճͯ͘͠Εͳ͍ ◦ ϦΫΤετ͢Δ͚ͩ Postman 34
  35. Bengo4.com, Inc. GUI: Postman Test 35

  36. Bengo4.com, Inc. CLI: Newman Test 36 $ npm i newman

    $ npx newman run echo.postman_collection.json ❏ Auth: Digest ↳ Delete Cookies GET https://postman-echo.com/cookies/delete?foo1=&foo2= [200 OK, 364B, 1171ms] ✓ Status code is 302 or 200 ✓ Body contains cookies 1. Body contains cookie foo1 2. Body contains cookie foo2 ✓ foo1 cookie is set ✓ foo2 cookie is set
  37. Bengo4.com, Inc. • API Buleprint͔ΒOpenAPIʹରԠ ◦ OepnAPI v3͸·ͩExperimental feature •

    ΦϒδΣΫτͷϓϩύςΟ΋νΣοΫ Dredd (Http API Testing Framework) 37 $ npm i dredd $ npx dredd ./pet2.yml localhost:4010 fail: GET (200) / duration: 64ms info: Displaying failed tests... fail: GET (200) / duration: 64ms fail: body: At '/status' Missing required property: status
  38. PostmanͰ APIςετΛߏங͢Δ 38

  39. Bengo4.com, Inc. PostmanςετͷྲྀΕ 39 OASΛॻ͘ TestγφϦΦ ΛJSͰbuild ςετ࣮ߦ

  40. Bengo4.com, Inc. • APIϦΫΤετ࣌ʹϨεϙϯεͰςετՄೳ • Postmanͷઃఆ͸Json͕ͩJavaScript͕ࠩ͠ࠐΊΔ ◦ Chai Postman͸JSͰςετεΫϦϓτ͕ઃఆՄೳ 40

    pm.test("εςʔλείʔυ͕ 200", function () { pm.response.to.have.status(200); });
  41. Bengo4.com, Inc. ྫ1. HTTPεςʔλείʔυ͚ͩςετ 41 const convertData = conversionResult.output[0].data; convertData.event.push({

    listen: "test", script: { exec: [ 'pm.test("Ϩεϙϯε͸εςʔλε ίʔυ͕ 200ܥ Ͱ੒ޭ", function () {', " pm.expect(pm.response.code).to.be.oneOf([200, 201, 202]);", "});", ], type: "text/javascript", }, });
  42. Bengo4.com, Inc. • Apideck͕ఏڙ͍ͯ͠ΔϥΠϒϥϦ • OAS͔ΒࣗಈͰςετΞαʔγϣϯΛࠩ͠ࠐΉ ◦ OAS v2, v3ରԠ(v3.1ඇରԠ)

    ྫ2.PortmanͰσʔλܕ΋ࡉ͔͘ςετ 42 ✓ [GET]::/pets/ - Status code is 2xx ✓ [GET]::/pets/ - Content-Type is application/json ✓ [GET]::/pets/ - Response has JSON Body ✓ [GET]::/pets/ - Schema is valid
  43. Bengo4.com, Inc. PortmanͰ૊ΈཱͯΒΕΔεΩʔϚݕূJS 43 // Response Validation const schema =

    { "type":"object","required":["status"],"properties":{"status":{"type":"string"}} } // Validate if response matches JSON schema pm.test("[GET]::/ - Schema is valid", function() { pm.response.to.have.jsonSchema( schema, {unknownFormats: ["int32", "int64", "float", "double"]} ); });
  44. 1. શମͷίʔυςετઓུΛཱͯΔ 2. ςετ࣮ߦ࣌ʹDIͰ෭࡞༻ΛݮΒ͢ 3. OASͰશͯͷσʔλʹexample APIςετΛࣗಈੜ੒͢ΔͨΊͷ 3ͭͷϙΠϯτ 44

  45. Bengo4.com, Inc. • OAS͔Βࣗಈੜ੒Ͱ͖Δͷ͸ɺ200ܥͷ੒ޭςετ ◦ 400ܥ΋࡞ΕΔ͕֦ு͕ඞཁ • ͦΕͧΕͷςετͰΧόʔ͢ΔൣғΛܾΊΔ ◦ ୯ମςετ

    ◦ APIςετ ◦ E2Eςετ ϙΠϯτ1. શମͷίʔυςετઓུΛཱͯΔ 45
  46. Bengo4.com, Inc. • ೝূ΍DBͳͲ͸DIͯ͠mock͢Δ ◦ ςετ͕࢝ΊΔ࣌ɺσʔλ͸͍ͭ΋ಉ͡ ◦ τϥϯβΫγϣϯ͸ͬͯɺ࠷ޙʹϩʔϧόοΫ • ॱংґଘ͕͓͖ͳ͍

    ϙΠϯτ2.1ͭͷςετ಺Ͱ෭࡞༻Λด͜͡ΊΔ 46
  47. Bengo4.com, Inc. • example͔ΒϦΫΤε τΛੜ੒͢ΔͨΊ • ࣄલʹ༻ҙͨ͠Mock σʔλͱ߹ΘͤΔ ϙΠϯτ3.OASͰશͯͷσʔλʹexample 47

    /pets/{petId}: get: operationId: showPetById parameters: - name: petId in: path example: 1 required: true schema: type: integer
  48. ·ͱΊ 48

  49. 1. OpenAPI GeneratorͰAPIΫϥΠΞϯτΛੜ੒ˍ ഑෍&ࣗ਎ΛറΔ 2. APIςετΛࣗಈੜ੒&CIͰ࣮ߦ͢Δ εΩʔϚۦಈ։ൃΛ੒ޭͤ͞Δ ϙΠϯτ 49

  50. Bengo4.com, Inc. • ࢓༷͕࠷ॳʹ͔ͬ͠Γܾ·ΔͷͰΘ͔Γ΍͍͢ • OAS͔ΒίʔυΛࣗಈੜ੒Ͱ͖ɺ։ൃίετ࡟ݮ ◦ API ΫϥΠΞϯτɺAPIςετɺυΩϡϝϯτ •

    ίΞͷ࣮૷ʹूதͰ͖Δ εΩʔϚۦಈ։ൃΛ࢝Ί·ͤΜ͔ʁ 50
  51. Bengo4.com, Inc. 51 એ఻ ࠾༻΍ͬͯ·͢!! ͸ͯϒͰൃ৴ͯ͠·͢!! creators.bengo4.com

  52. END ͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ 52

  53. Bengo4.com, Inc. • Npm Library ◦ openapi-to-postmanv2 - npm ◦

    newman - npm ◦ @apideck/portman - npm ◦ dredd - npm • OpenAPI ◦ OpenAPI-Specification/petstore.yaml at main ◦ What Is the Difference Between Swagger and OpenAPI? ◦ What's the Difference Between OpenAPI 2.0, 3.0, and 3.1? • OpenAPIͷΤίγεςϜ ◦ OpenAPI.Tools ◦ GitHub - Redocly/redoc ◦ GitHub - OpenAPITools/openapi-generator ◦ Prism | Open-Source HTTP Mock and Proxy Server | Stoplight • Postman ◦ Test script examples | Postman Learning Center ࢀߟURL 53