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

Swaggerで定義したAPI仕様から Retrofitで使用するinterfaceを自動生成してみる

Swaggerで定義したAPI仕様から Retrofitで使用するinterfaceを自動生成してみる

Ryosuke Horie

November 28, 2017
Tweet

More Decks by Ryosuke Horie

Other Decks in Programming

Transcript

  1. © 2017 VASILY,Inc.
    4XBHHFSͰఆٛͨ͠"1*࢓༷͔Β
    3FUSPpUͰ࢖༻͢ΔJOUFSGBDFΛࣗಈੜ੒ͯ͠ΈΔ
    2017/11/28 potatotips #45
    @Horie1024

    View Slide

  2. © 2017 VASILY,Inc.
    ࣗݾ঺հ
    w !)PSJF
    w 7"4*-: *OD
    w "OESPJEΤϯδχΞ
    w ϏʔϧɺࣗಈԽ޷͖

    View Slide

  3. © 2017 VASILY,Inc.
    ೥݄;0;0508/΍8&"3ΛӡӦ͢Δ
    ελʔττΡσΠάϧʔϓʹࢀը ࢠձࣾԽ

    View Slide

  4. © 2017 VASILY,Inc.
    ࠓ೔࿩͢͜ͱ
    w TXBHHFSQBSTFSΛ࢖͏ͱ4XBHHFSͰఆٛͨ͠"1*
    ࢓༷Λίʔυ͔Β؆୯ʹѻ͑Δ
    w LPMUJOQPFUͱ૊Έ߹ΘͤΔͱ3FUSPpUͰ࢖༻͢Δ
    EBUBDMBTT΍JOUFSGBDFͷίʔυΛੜ੒Ͱ͖Δ
    w (SBEMF1MVHJOԽ͢Δͱ௙Δ

    View Slide

  5. © 2017 VASILY,Inc.
    Ϟνϕʔγϣϯ
    w σʔλ૚ͷίʔυΛࣗಈੜ੒͍ͨ͠

    View Slide

  6. © 2017 VASILY,Inc.
    Ͳ͏࣮ݱ͢Δʁ
    w ฐࣾͷ"1*࢓༷͸4XBHHFSͰఆ͍ٛͯ͠Δ

    View Slide

  7. © 2017 VASILY,Inc.
    Ͳ͏࣮ݱ͢Δʁ
    w ฐࣾͷ"1*࢓༷͸4XBHHFSͰఆ͍ٛͯ͠Δ
    w LPUMJOQPFUΛ࢖ͬͯΈ͍ͨ

    View Slide

  8. © 2017 VASILY,Inc.
    Ͳ͏࣮ݱ͢Δʁ
    w ฐࣾͷ"1*࢓༷͸4XBHHFSͰఆ͍ٛͯ͠Δ
    w LPUMJOQPFUΛ࢖ͬͯΈ͍ͨ
    w 4XBHHFSͰఆٛͨ͠"1*࢓༷͔Βੜ੒͠Α͏ʂ

    View Slide

  9. © 2017 VASILY,Inc.
    4XBHHFSͰఆٛͨ͠"1*࢓༷ͷύʔε
    w TXBHHFSBQJTXBHHFSQBSTFSΛ࢖͏ͱ؆୯
    w TXBHHFSKTPOΛಡΈࠐ·ͤΔඞཁ͕͋Δ
    w ϩʔΧϧͰ΋αʔό্ͷ΋ͷͰ΋0,
    // SwaggerΦϒδΣΫτ͔ΒAPI࢓༷ʹΞΫηε
    val swagger: Swagger = SwaggerParser().read(“swagger.json”)

    View Slide

  10. © 2017 VASILY,Inc.
    LPUMJOQPFUͰͷίʔυੜ੒
    w TRVBSFLPUMJOQPFUͰੜ੒
    w 4XBHHFSΦϒδΣΫτ͔Βඞཁͳ৘ใΛऔಘ
    w LPUMJOQPFUͰίʔυੜ੒
    w ೚ҙͷύεʹॻ͖ग़͠

    View Slide

  11. © 2017 VASILY,Inc.
    ྫ(JU)VC"1*-JTUVTFSSFQPTJUPSJFT

    View Slide

  12. © 2017 VASILY,Inc.
    4XBHHFSͰ"1*࢓༷Λఆٛ
    "paths": {
    "/users/{username}/repos": {
    "get": {
    "summary": "List public repositories for the specified
    user.",
    "operationId": "listRepos",
    "parameters": [
    {
    "name": "username",
    "in": "path",
    "description": "ID of pet to update",
    "required": true,
    "type": "string"
    },
    {
    "name": "type",
    "in": "query",
    "description": "Can be one of all, owner, member.
    Default: owner",
    "required": false,
    "type": "string"
    },
    {
    "name": "sort",
    "in": "query",
    "description": "Can be one of created, updated,
    pushed, full_name. Default: full_name",
    "required": false,
    "type": "string"
    },
    {
    "name": "direction",
    "in": "query",
    "description": "Can be one of asc or desc.
    Default: when using full_name: asc, otherwise desc",
    "required": false,
    "type": "string"
    }
    ],
    "tags": [
    "Users"
    ],
    "responses": {
    "200": {
    "description": "successful operation",
    "schema": {
    "type": "array",
    "items": {
    "$ref": "#/definitions/Repo"
    }
    }
    },
    "400": {
    "description": "Invalid status value"
    }
    }
    }
    }
    }

    View Slide

  13. © 2017 VASILY,Inc.
    w ͜Μͳײ͡ͷίʔυΛੜ੒͍ͨ͠
    w 4XBHHFSΦϒδΣΫτ͔Βඞཁͳ৘ใΛऔಘ
    w LPUMJOQPFUͰίʔυੜ੒
    w ೚ҙͷύεʹॻ͖ग़͠
    LPUMJOQPFUͰͷίʔυੜ੒
    interface UsersService {
    @GET("/users/{username}/repos")
    fun listRepos(
    @Path("username") username: String,
    @Query("type") type: String? = null,
    @Query("sort") sort: String? = null,
    @Query("direction") direction: String? = null
    ): Single>
    }

    View Slide

  14. © 2017 VASILY,Inc.
    ྫ6TFS4FSWJDFJOUFSGBDFͷੜ੒
    FileSpec.builder("", "UserService")
    .addType(TypeSpec
    .interfaceBuilder("UserService")
    .addFunction(FunSpec.builder("listRepos")
    .addModifiers(KModifier.ABSTRACT).build()
    ).build()
    ).build()
    interface UsersService {
    fun listRepos()
    }

    View Slide

  15. © 2017 VASILY,Inc.
    ྫ6TFS4FSWJDFJOUFSGBDFͷੜ੒
    FileSpec.builder("", "UserService")
    .addType(TypeSpec
    .interfaceBuilder("UserService")
    .addFunction(FunSpec.builder("listRepos")
    .addModifiers(KModifier.ABSTRACT)
    .addAnnotation(AnnotationSpec.builder(GET::class)
    .addMember("\"/users/{username}/repos\"")
    .build()).build()
    ).build()
    ).build().writeTo(System.out)
    interface UserService {
    @GET("/users/{username}/repos")
    fun listRepos()
    }

    View Slide

  16. © 2017 VASILY,Inc.
    ྫ6TFS4FSWJDFJOUFSGBDFͷੜ੒
    FileSpec.builder("", "UserService")
    .addType(TypeSpec
    .interfaceBuilder("UserService")
    .addFunction(FunSpec.builder("listRepos")
    .addModifiers(KModifier.ABSTRACT)
    .addAnnotation(AnnotationSpec.builder(GET::class)
    .addMember("\"/users/{username}/repos\"")
    .build())
    .returns(ParameterizedTypeName.get(Single::class.asTypeName(),
    ParameterizedTypeName.get(List::class.asTypeName(),
    ClassName("", "Repo"))
    )).build()
    ).build()
    ).build()
    interface UserService {
    @GET("/users/{username}/repos")
    fun listRepos(): Single>
    }

    View Slide

  17. © 2017 VASILY,Inc.
    ྫ6TFS4FSWJDFJOUFSGBDFͷੜ੒
    FileSpec.builder("", "UserService")
    .addType(TypeSpec
    .interfaceBuilder("UserService")
    .addFunction(FunSpec.builder("listRepos")
    .addModifiers(KModifier.ABSTRACT)
    .addAnnotation(AnnotationSpec.builder(GET::class)
    .addMember("\"/users/{username}/repos\"")
    .build())
    .addParameter(ParameterSpec.builder("username", String::class.asTypeName().asNonNullable())
    .addAnnotation(AnnotationSpec.builder(Path::class).addMember("\"username\"").build())
    .build())
    .returns(ParameterizedTypeName.get(Single::class.asTypeName(),
    ParameterizedTypeName.get(List::class.asTypeName(),
    ClassName("", "Repo"))
    )).build()
    ).build()
    ).build()
    interface UserService {
    @GET("/users/{username}/repos")
    fun listRepos(@Path("username") username: String): Single>
    }

    View Slide

  18. © 2017 VASILY,Inc.
    LPUMJOQPFUͰͷίʔυੜ੒
    interface UsersService {
    @GET("/users/{username}/repos")
    fun listRepos(
    @Path("username") username: String,
    @Query("type") type: String? = null,
    @Query("sort") sort: String? = null,
    @Query("direction") direction: String? = null
    ): Single>
    }
    "paths": {
    "/users/{username}/repos": {
    "get": {
    "operationId": "listRepos",
    "parameters": [
    {
    "name": "username",
    "in": "path",
    "description": "ID of pet to update",
    "required": true,
    "type": "string"
    },
    {
    "name": "type",
    "in": "query",
    "required": false,
    "type": "string"
    },
    {
    "name": "sort",
    "in": "query",
    "required": false,
    "type": "string"
    },
    {
    "name": "direction",
    "in": "query",
    "required": false,
    "type": "string"
    }
    ],
    "tags": [
    "Users"
    ],

    View Slide

  19. © 2017 VASILY,Inc.
    LPUMJOQPFUͰͷίʔυੜ੒
    interface UsersService {
    @GET("/users/{username}/repos")
    fun listRepos(
    @Path("username") username: String,
    @Query("type") type: String? = null,
    @Query("sort") sort: String? = null,
    @Query("direction") direction: String? = null
    ): Single>
    }
    "paths": {
    "/users/{username}/repos": {
    "get": {
    "operationId": "listRepos",
    "parameters": [
    {
    "name": "username",
    "in": "path",
    "description": "ID of pet to update",
    "required": true,
    "type": "string"
    },
    {
    "name": "type",
    "in": "query",
    "required": false,
    "type": "string"
    },
    {
    "name": "sort",
    "in": "query",
    "required": false,
    "type": "string"
    },
    {
    "name": "direction",
    "in": "query",
    "required": false,
    "type": "string"
    }
    ],
    "tags": [
    "Users"
    ],

    View Slide

  20. © 2017 VASILY,Inc.
    LPUMJOQPFUͰͷίʔυੜ੒
    interface UsersService {
    @GET("/users/{username}/repos")
    fun listRepos(
    @Path("username") username: String,
    @Query("type") type: String? = null,
    @Query("sort") sort: String? = null,
    @Query("direction") direction: String? = null
    ): Single>
    }
    "paths": {
    "/users/{username}/repos": {
    "get": {
    "operationId": "listRepos",
    "parameters": [
    {
    "name": "username",
    "in": "path",
    "description": "ID of pet to update",
    "required": true,
    "type": "string"
    },
    {
    "name": "type",
    "in": "query",
    "required": false,
    "type": "string"
    },
    {
    "name": "sort",
    "in": "query",
    "required": false,
    "type": "string"
    },
    {
    "name": "direction",
    "in": "query",
    "required": false,
    "type": "string"
    }
    ],
    "tags": [
    "Users"
    ],

    View Slide

  21. © 2017 VASILY,Inc.
    LPUMJOQPFUͰͷίʔυੜ੒
    interface UsersService {
    @GET("/users/{username}/repos")
    fun listRepos(
    @Path("username") username: String,
    @Query("type") type: String? = null,
    @Query("sort") sort: String? = null,
    @Query("direction") direction: String? = null
    ): Single>
    }
    "paths": {
    "/users/{username}/repos": {
    "get": {
    "operationId": "listRepos",
    "parameters": [
    {
    "name": "username",
    "in": "path",
    "description": "ID of pet to update",
    "required": true,
    "type": "string"
    },
    {
    "name": "type",
    "in": "query",
    "required": false,
    "type": "string"
    },
    {
    "name": "sort",
    "in": "query",
    "required": false,
    "type": "string"
    },
    {
    "name": "direction",
    "in": "query",
    "required": false,
    "type": "string"
    }
    ],
    "tags": [
    "Users"
    ],

    View Slide

  22. © 2017 VASILY,Inc.
    LPUMJOQPFUͰͷίʔυੜ੒
    interface UsersService {
    @GET("/users/{username}/repos")
    fun listRepos(
    @Path("username") username: String,
    @Query("type") type: String? = null,
    @Query("sort") sort: String? = null,
    @Query("direction") direction: String? = null
    ): Single>
    }
    "paths": {
    "/users/{username}/repos": {
    "get": {
    "operationId": "listRepos",
    "parameters": [
    {
    "name": "username",
    "in": "path",
    "description": "ID of pet to update",
    "required": true,
    "type": "string"
    },
    {
    "name": "type",
    "in": "query",
    "required": false,
    "type": "string"
    },
    {
    "name": "sort",
    "in": "query",
    "required": false,
    "type": "string"
    },
    {
    "name": "direction",
    "in": "query",
    "required": false,
    "type": "string"
    }
    ],
    "tags": [
    "Users"
    ],

    View Slide

  23. © 2017 VASILY,Inc.
    LPUMJOQPFUͰͷίʔυੜ੒
    interface UsersService {
    @GET("/users/{username}/repos")
    fun listRepos(
    @Path("username") username: String,
    @Query("type") type: String? = null,
    @Query("sort") sort: String? = null,
    @Query("direction") direction: String? = null
    ): Single>
    }
    "responses": {
    "200": {
    "description": "successful operation",
    "schema": {
    "type": "array",
    "items": {
    "$ref": "#/definitions/Repo"
    }
    }
    },
    "400": {
    "description": "Invalid status value"
    }
    }
    }
    }
    },
    "definitions": {
    "Repo": {
    "type": "object",
    "properties": {
    "id": {
    "type": "string"
    },
    "name": {
    "type": "string"
    },
    "url": {
    "type": "string"
    }
    }
    }
    }

    View Slide

  24. © 2017 VASILY,Inc.
    LPUMJOQPFUͰͷίʔυੜ੒
    interface UsersService {
    @GET("/users/{username}/repos")
    fun listRepos(
    @Path("username") username: String,
    @Query("type") type: String? = null,
    @Query("sort") sort: String? = null,
    @Query("direction") direction: String? = null
    ): Single>
    }
    "responses": {
    "200": {
    "description": "successful operation",
    "schema": {
    "type": "array",
    "items": {
    "$ref": "#/definitions/Repo"
    }
    }
    },
    "400": {
    "description": "Invalid status value"
    }
    }
    }
    }
    },
    "definitions": {
    "Repo": {
    "type": "object",
    "properties": {
    "id": {
    "type": "string"
    },
    "name": {
    "type": "string"
    },
    "url": {
    "type": "string"
    }
    }
    }
    }

    View Slide

  25. © 2017 VASILY,Inc.
    LPUMJOQPFUͰͷίʔυੜ੒
    data class Repo(
    val id: String,
    val name: String,
    val url: String
    )
    "responses": {
    "200": {
    "description": "successful operation",
    "schema": {
    "type": "array",
    "items": {
    "$ref": "#/definitions/Repo"
    }
    }
    },
    "400": {
    "description": "Invalid status value"
    }
    }
    }
    }
    },
    "definitions": {
    "Repo": {
    "type": "object",
    "properties": {
    "id": {
    "type": "string"
    },
    "name": {
    "type": "string"
    },
    "url": {
    "type": "string"
    }
    }
    }
    }

    View Slide

  26. © 2017 VASILY,Inc.
    (SBEMF1MVHJOԽ
    w ͪ͜ΒͷϖʔδΛࢀߟʹ,PUMJOͰ࣮૷
    w IUUQTXXXUIFESPJETPOSPJETDPNCMPH
    IPXUPDSFBUFHSBEMFQMVHJOJOLPUMJO
    w ɹɹɹɹɹɹɹɹɹɹɹͰੜ੒Λ࣮ߦ
    ./gradlew generateDataLayer

    View Slide

  27. © 2017 VASILY,Inc.
    σϞ

    View Slide

  28. © 2017 VASILY,Inc.
    ·ͱΊ
    w TXBHHFSQBSTFSΛ࢖͏ͱ4XBHHFSͰఆٛͨ͠"1*
    ࢓༷Λίʔυ͔Β؆୯ʹѻ͑Δ
    w LPUMJOQPFUΛ࢖͏ͱ,PMUJOͷίʔυΛ؆୯ʹੜ੒Ͱ
    ͖Δ
    w (SBEMF1MVHJO΋,PUMJOͰॻָ͚͍ͯ͠

    View Slide

  29. © 2017 VASILY,Inc.
    ͋Γ͕ͱ͏͍͟͝·ͨ͠

    View Slide