Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swagger documentation for BodyParam and Responses with JSON models generated from case classes by reflections #653

Closed
wants to merge 0 commits into from

Conversation

smeagol74
Copy link
Contributor

This patch allows to document JSON models from case classes with Swagger like this example:

package quickstart.action

import java.util.Date

import xitrum.FutureAction
import xitrum.annotation.{POST, Swagger}
import xitrum.util.SeriDeseri

@POST("api/test")
@Swagger(
	Swagger.JsonBody("request", "This is request model", Test.RequestType),
	Swagger.Response(200, "Success", Some(Test.ResponseType)),
	Swagger.Response(201, "Success", Some(Test.ResponseArrayType)),
	Swagger.Response(404, "Not found")
)
class Test extends FutureAction {
	override def execute(): Unit = {
		SeriDeseri.fromJValue[Test.Request](requestContentJValue) match {
			case Some(req) => // do something useful
			case None => // reply 404 or something
		}
	}
}

object Test {

	case class Sub(subValue: Option[Int])

	case class Response(value: String, sub: Sub)

	case class Request(integer: Int,
	                   long: Long,
	                   float: Float,
	                   double: Double,
	                   string: String,
	                   boolean: Boolean,
	                   dateTime: Date,
	                   optional: Option[String],
	                   list: List[String],
	                   seq: Seq[Sub],
	                   obj: Sub
	                  )

	val RequestType = Swagger.arrayOf[Request]
	val ResponseType = Swagger.valueOf[Response]
	val ResponseArrayType = Swagger.arrayOf[Response]
}

And this code will produce following swagger.json

{
  "swagger" : "2.0",
  "info" : {
    "title" : "APIs documented by Swagger",
    "version" : "1.0-SNAPSHOT"
  },
  "basePath" : "/",
  "paths" : {
    "/api/test" : {
      "post" : {
        "operationId" : "test",
        "parameters" : [ {
          "name" : "request",
          "in" : "body",
          "description" : "This is request model",
          "required" : true,
          "schema" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/definitions/Test.Request"
            }
          }
        } ],
        "responses" : {
          "200" : {
            "description" : "Success",
            "schema" : {
              "$ref" : "#/definitions/Test.Response"
            }
          },
          "201" : {
            "description" : "Success",
            "schema" : {
              "type" : "array",
              "items" : {
                "$ref" : "#/definitions/Test.Response"
              }
            }
          },
          "404" : {
            "description" : "Not found"
          }
        }
      }
    }
  },
  "definitions" : {
    "Test.Request" : {
      "type" : "object",
      "properties" : {
        "integer" : {
          "type" : "integer",
          "format" : "int32"
        },
        "long" : {
          "type" : "integer",
          "format" : "int64"
        },
        "float" : {
          "type" : "number",
          "format" : "float"
        },
        "double" : {
          "type" : "number",
          "format" : "double"
        },
        "string" : {
          "type" : "string",
          "format" : ""
        },
        "boolean" : {
          "type" : "boolean",
          "format" : ""
        },
        "dateTime" : {
          "type" : "string",
          "format" : "date-time"
        },
        "optional" : {
          "type" : "string",
          "format" : ""
        },
        "list" : {
          "type" : "array",
          "items" : {
            "type" : "string",
            "format" : ""
          }
        },
        "seq" : {
          "type" : "array",
          "items" : {
            "$ref" : "#/definitions/Test.Sub"
          }
        },
        "obj" : {
          "$ref" : "#/definitions/Test.Sub"
        }
      },
      "required" : [ "integer", "long", "float", "double", "string", "boolean", "dateTime", "list", "seq", "obj" ]
    },
    "Test.Sub" : {
      "type" : "object",
      "properties" : {
        "subValue" : {
          "type" : "integer",
          "format" : "int32"
        }
      }
    },
    "Test.Response" : {
      "type" : "object",
      "properties" : {
        "value" : {
          "type" : "string",
          "format" : ""
        },
        "sub" : {
          "$ref" : "#/definitions/Test.Sub"
        }
      },
      "required" : [ "value", "sub" ]
    }
  }
}

You may want to load that json to Swagger Online Editor to check how the documentation looks like.

This patch supports simple case classes as models (not sure about inheritance and other advanced features), Seq, List, Set as array types, Option for non-mandatory fields.

.editorconfig Outdated
@@ -0,0 +1,7 @@
[*]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you remove this file? I guess it's only for a particular editor thus should not be committed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is cross editors configuration for code autoformatting supported by rather a lot of IDEs and editors: https://editorconfig.org/#download

The idea was to allow to share the same configuration between different project developers. But if you insist, I'll remove it.

*
* @param name field name
* @param tpe field type
* @param isRequired_? if true means that field is required
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the purpose to add _? to the name? Should we simply name it required?


/**
* Utility functions to reflect scala case classes to structure that can be rendered to Swagger documentation
* User: smeagol74
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anyone can freely change any file, so let's remove User, Date, and Time.

def isArrayType_?(tpe: Type): Boolean = _arraySymbols.contains(tpe.typeSymbol.name)


def _reflectField(fld: TermSymbol): Swagger.JsonField = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove _ prefix because is ugly. Add private if it's private.

@smeagol74
Copy link
Contributor Author

@ngocdaothanh , I applied all the fixes. Also I rebased my patch over the current master and squashed into the single commit. I did not removed the .editorconfig yet, hope you will decide to leave it in project as useful.

@ngocdaothanh
Copy link
Member

👍 Thanks, I've commented at the new PR. The PR is very cool, I'm just requesting for some changes in coding style, so that the new files look consistent with existing files.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants