-
Notifications
You must be signed in to change notification settings - Fork 58
App Developers: Swagger Best Practices
Excellent API documentation is crucial to a user's ability to easily and successfully use your app's Cytoscape Commands and Functions. Cytoscape enables users to discover and understand APIs (including your app's API) via the Help --> Automation menu items, which display Swagger documents in a browser.
This page explains Cytoscape API conventions that app developers can use to produce Swagger document pages that in turn enable users to quickly understand and apply Cytoscape APIs.
For each API, users benefit most if the documentation explains the purpose and context of the API, parameters that can be used, and results that can be returned. Swagger itself takes care of implementing the Try it out! button that allows users to experiment with an API directly.
The audience for Swagger documents is both the new and expert user, with the new user trying to understand if and how the API can be used as a component of a specific workflow. The expert user will likely focus on the presence and form of specific parameters and return results.
In spirit, we model our documentation after the highly successful *NIX man pages, where every page has standard layout and content:
- Command/Function name
- Narrative describing intended use and context, and details of actual usage
- List of parameters and meanings
- Return results
Swagger allows pages to have a wide range of content, including virtually no content at all. Rich documentation enables users to make good use of your APIs; poor documentation likely leads to users passing your APIs over.
To position your apps (and Cytoscape Automation in general) to best serve both new and experienced users, app writers should spend time developing meaningful and unambiguous content for each of these four categories, then packaging it using Swagger facilities as described below.
Combining this content with Swagger's availability, layout and Try it now button will enable users to spend time developing their workflows instead of trying to understand app Commands and Functions.
An app should include documentation for each endpoint it exposes, where the documentation explains the intended use, context, and details of actual usage. In addition, it should describe each parameter and the endpoint result concisely. This saves the reader from having to guess purpose and usage from names and titles. Swagger allows the following tags for these purposes:
Tag | Descriptive Fields |
---|---|
@Api | name |
@ApiOperation | value,notes |
@ApiParam | value |
@ApiResponse | message |
@ApiModel | value,description |
@ApiModelProperty | value |
@ApiImplicitParams | list of @ApiImplicitParam |
@ApiImplicitParam | value,datatype,paramtype |
Swagger tags allow the option of using Markdown syntax to enhance the appearance of your documentation, and allows you to include code snippets, text formatting, and HTTP links.
Note that while Swagger uses tags beginning with @Api
to construct page content, it also leverages JAX-RS tags (e.g., @Path
, @Produces
, @Consumes
) to supplement the page with additional HTTP-oriented content. JAX-RS tags define the actual endpoint so it can be called; their use and function are described in Adding Automation to Existing Apps.
For the sake of organization in the Swagger UI, your app should be assigned a tag consisting of App:
followed by the actual name of the app. This Swagger tag enables Swagger to group pages for app operations under a single heading, thus making them easier to find.
A Java snippet from the Best Practices Sample App illustrates:
@Api(tags="Apps: Best Practices")
@Path("/cyrestbestpractices/v1/classroom/")
public interface ClassroomResource {
...
}
This Swagger tag produces the 'Apps: Best Practices' heading, as shown in the Swagger UI below:

Individual app operations can be found by expanding the 'Apps: Best Practices' heading.
App authors should use operation tags to provide a narrative describing intended use and context of an endpoint, and the specifics of its use in practice, and a description of parameters and return results. For example:
@ApiOperation(
value = "Replace the teacher",
notes = "Replaces the classes teacher.\n\nYou can use this to 'edit' the teacher's information by replacing their entire record with a newer one.\n\nBest not to do this before a holiday.",
response=Person.class)
@Path("teacher")
@PUT
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Person putTeacher(
@ApiParam(value = "The new teacher data", required = true)
Person teacher
);
The @ApiOperation
tag identifies the endpoint name and response type, and the @ApiParam
describes the teacher parameter.
An app author should use Swagger model tags to describe the structure and contents of any classes involved in parameter values and return results. Such classes are assumed to be POJOs (Plain Old Java Objects). For example:
@ApiModel(value="Person", description="Defines a person by name and age")
public class Person {
@ApiModelProperty(value = "The persons's first name", example="Jeff")
public String firstName;
@ApiModelProperty(value = "The persons's last name", example="Winger")
public String lastName;
@ApiModelProperty(value = "The persons's age at the start of the semester", example="42")
public int age;
}
The @ApiModel
tag describes the class, and the @ApiModelProperty
describes class members.
This is the Swagger page produced for the Replace the teacher operation described above. Note that it includes a description of the Person model as the function result.

Swagger can use POJOs to provide example JSON for input and output parameters. If your method returns a POJO, Swagger will use that POJO's class to generate a model for it, and it will appear on the Swagger page.
Apps that consume or produce POJOs should use Swagger tags to document those models so users can see them embedded in Swagger pages.
The two code snippets above, Operation Tags and Model Tags, indicate that the putTeacher
operation uses the Person model, and then adds documentation and examples to that model.
As a result, the Documented Swagger UI Operation includes descriptions of the input and output, as well as common example values.
Swagger allows you to provide examples for fields inside your models using the example
field in the @ApiModelProperty
tag. If possible, make these examples such that they will always be valid (the operation will always execute with them, regardless of the conditions). If you cannot ensure the validity of an example value, use one that a user can reasonably expect to see, and note the requirements in the value
field of the ApiModelProperty
tag.
You can also limit the expected values to an explicit set by using the allowableValues
field of the @ApiModelProperty
tag. Note that this will only be enforced by the Swagger UI, and your code must still account for all possible values.
Note that endpoint paths may contain fields to be filled in. For example:
{networkSUID}/views/{networkViewSUID}/diffuse_with_options
specifies that networkSUID and networkViewSUID will be replaced by values that will be recognized only in a running instance of Cytoscape. In such cases, it is not possible to put example values in your documentation, as they depend on an execution environment that may be dynamic.
Regardless, your field description should explain how or where values may be found for these fillins, thereby giving the user the best chance of quickly and easily using your endpoint.
For parameters that are primitives or Swagger-annotated POJOs, the combination of @ApiParam and JAX-RS tags inform Swagger of the data models to be included in an operation’s Swagger page. However, it may occasionally be necessary to accept input parameters in a more efficient manner, such as streaming data. An example of this is the java.io.InputStream
parameter used in CyREST’s TableResource
class.
public Response createColumn(
@ApiParam(value="Network ID") @PathParam("networkId") Long networkId,
@ApiParam(value="Table Type", allowableValues="defaultnode,defaultedge,defaultnetwork")
@PathParam("tableType") String tableType,
… final InputStream is) {
...
}
Within the createColumn
method, this parameter is used to read a JSON-encoded stream, which is more efficient than loading an entire data model into memory. If we we were to leave the method without additional annotations, Swagger would be able to provide little information on the expected structure of the input stream.
Fortunately, we can do this explicitly. We can describe the Model for this JSON-encoded stream using the org.cytoscape.rest.internal.model.NewColumn POJO, and include this type using the @ApiImplicitParams annotation.
@Path("/v1/networks/{networkId}/tables")
public class TableResource extends AbstractResource {
...
@POST
@Path("/{tableType}/columns")
@Consumes(MediaType.APPLICATION_JSON)
@ApiOperation(value="Create new column(s) in the table",
notes="Creates a new, empty column in an assigned table.\n\n"
+ "This resource can also accepts an array of NewColumn objects to "
+ "create multiple columns via the 'is' parameter.")
@ApiImplicitParams(value= {
@ApiImplicitParam(value="New Column Info",
dataType="org.cytoscape.rest.internal.model.NewColumn",
paramType="body",
required=true),
})
@ApiResponses (value = {
@ApiResponse(code=201, message="Column(s) created"),
@ApiResponse(code=412, message="Could not process column JSON")
})
public Response createColumn(
@ApiParam(value="Network ID") @PathParam("networkId") Long networkId,
@ApiParam(value="Table Type", allowableValues="defaultnode,defaultedge,defaultnetwork")
@PathParam("tableType") String tableType,
@ApiParam(hidden=true) final InputStream is) {
...
}
In the snippet above, the caller uses the /v1/networks/{networkId}/tables/{tableType}/columns
to call the createColumn
function. The {networkId}
component maps to the networkID parameter, and the {tableType}
component maps to the tableType parameter. In both cases, the @ApiParam
provides the Swagger documentation information.
According to the JAX-RS rules, the is parameter is bound to the HTTP payload. The app author intends that the payload contain JSON encoding an org.cytoscape.rest.internal.model.NewColumn
instance, and will enforce this at runtime. The app author documents this in the @ApiOperation
tag and uses the @ApiImplicitParams
tag to notify Swagger to assist the user by including the org.cytoscape.rest.internal.model.NewColumn
definition on the Swagger page.
@ApiImplicitParams
should be used to include documentation for any non-modeled type used by any parameter.
For return values that are primitives or Swagger-annotated POJOs, Swagger can infer the data model to be included in an operation’s Swagger page. However, it may occasionally be necessary to produce return values that customize features of the response payload, such as the response type (201, 404, etc), or provide streaming output instead of an entire data object. An example of a return value that uses customization is that of the Diffusion App’s diffuseWithOptions
method.
public Response diffuseWithOptions(@ApiParam(value = "Diffusion Parameters", required = true) DiffusionParameters diffusionParameters)
{
...
}
Within the diffuseWithOptions
method, a javax.ws.rs.core.Response
object is created and returned with a custom status code. If this method were left without additional annotations, Swagger would be able to provide little information on the expected structure of the return data.
As with input parameters, we can set output models explicitly. We can describe the model for our return data using the DiffusionAppResponse
class, and include this type using the response
field of the @ApiOperation
annotation.
@POST
@Produces("application/json")
@Consumes("application/json")
@Path("currentView/diffuse_with_options")
@ApiOperation(value = "Execute Diffusion Analysis on Current View with Options",
notes = GENERIC_SWAGGER_NOTES,
response = DiffusionAppResponse.class)
@ApiResponses(value = {@ApiResponse(code = 404, message = "Network or Network View does not exist", response = CIResponse.class)})
public Response diffuseWithOptions(@ApiParam(value = "Diffusion Parameters", required = true) DiffusionParameters diffusionParameters)
{
...
}
Note that
DiffusionAppResponse
must be derived from theCIResponse
in order to comply with Cytoscape Function Best Practices. It is defined as follows:
@ApiModel(value="Diffusion App Response", description="Diffusion Analysis Results in CI Format", parent=CIResponse.class)
public static class DiffusionAppResponse extends CIResponse<DiffusionResultColumns>{
}
When using Swagger to document a function it is advised to use the checklist below to evaluate if your command follows best practices in documentation.
Links in checklist headings will direct you to any relevant portions of the Best Practices document.
- Does your app tag start with the prefix "Apps: "?
- Does your app tag uniquely identify your app?
- Do all your operations have an
@ApiOperation
tag?
- Do all your
@ApiOperation
tags have avalue
field? - Do all these fields clearly identify the purpose of each operation?
- Do all these fields differentiate each operation from all the others in Cytoscape?
- Are all these fields sufficiently brief? They should all display without difficulty in the Swagger UI.
- Do all your
@ApiOperation
tags have anotes
field? - Do all these fields guide a user through using the operation?
- Do all these fields explain any necessary preconditions, multiple modes of execution, or parameter interdependencies?
- Do all these fields explain any conditions that can cause errors?
- Do any of your
@ApiOperation
annotations require aresponse
field? - Do all these fields point to well documented API Models?
- Does each API Model have a valid @ApiModel annotation?
- Does each @ApiModel annotation have a
value
field to differentiate it from other models? - Does each @ApiModel annotation have a
description
field to fully describe the model to the user?
- Does every public field in an API Model have a valid @ApiModelProperty annotation?
- Does each @ApiModelProperty annotation have a
value
field to differentiate it from other fields? - Does each @ApiModelProperty annotation have a
description
field to describe the field in prose? - Does each @ApiModelProperty annotation have an
example
field representative of a reasonable input?