├── .gitignore ├── README.md ├── pom.xml └── src └── main ├── java └── com │ └── jakubstas │ └── swagger │ ├── SpringWithSwagger.java │ ├── model │ ├── Employee.java │ ├── Product.java │ ├── ProductList.java │ ├── User.java │ └── UserList.java │ ├── rest │ ├── EmployeeEndpoint.java │ ├── ProductsEndpoint.java │ ├── UsersEndpoint.java │ └── config │ │ ├── AccessHiddenModelConverter.java │ │ ├── AccessHiddenSpecFilter.java │ │ └── SwaggerConfiguration.java │ └── service │ ├── EntityAlreadyExistsException.java │ ├── EntityNotFoundException.java │ ├── ProductService.java │ └── UserService.java ├── resources ├── avatars │ ├── 1.png │ └── 2.png ├── beans.xml ├── log4j.properties └── swagger.properties └── webapp ├── WEB-INF └── web.xml └── apidocs ├── css ├── reset.css └── screen.css ├── images ├── explorer_icons.png ├── logo_small.png ├── pet_store_api.png ├── throbber.gif └── wordnik_api.png ├── index.html ├── lib ├── backbone-min.js ├── handlebars-1.0.0.js ├── highlight.7.3.pack.js ├── jquery-1.8.0.min.js ├── jquery.ba-bbq.min.js ├── jquery.slideto.min.js ├── jquery.wiggle.min.js ├── shred.bundle.js ├── shred │ └── content.js ├── swagger-oauth.js ├── swagger.js └── underscore-min.js ├── o2c.html ├── swagger-ui.js └── swagger-ui.min.js /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.settings/ 3 | /.classpath 4 | /.project 5 | /.springBeans 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Spring with Swagger example 2 | ================= 3 | This project is a simple example of integration of Swagger with Spring 3 application (with Jersey 2). 4 | 5 | About 6 | ----- 7 | This project was created to enhance my microseries on integration of Swagger with Jersey 2 in Spring application. For further information check out following posts from series called Spring Rest API with Swagger: 8 | 9 | 1. [Integration and configuration](http://jakubstas.com/spring-jersey-swagger-configuration) 10 | 2. [Creating documentation](http://jakubstas.com/spring-jersey-swagger-create-documentation) 11 | 3. [Exposing documentation](http://jakubstas.com/spring-jersey-swagger-exposing-documentation) 12 | 4. [Fine-tuning exposed documentation](http://jakubstas.com/spring-jersey-swagger-fine-tuning-exposed-documentation) 13 | 14 | Try it out! 15 | ----------- 16 | * **Do it yourself** and build and deploy it using your favourite IDE 17 | * **Use embedded Jetty** and run it right away with `mvn jetty:run` 18 | 19 | Check following URLs (running this example on your localhost): 20 | 21 | 1. [Swagger API listing](http://localhost:8080/SpringWithSwagger/rest/api-docs/) 22 | 2. [Swagger API documentation](http://localhost:8080/SpringWithSwagger/rest/api-docs/products) 23 | 3. [Swagger UI](http://localhost:8080/SpringWithSwagger/apidocs/) 24 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | com.jakubstas.swagger 4 | SpringWithSwagger 5 | war 6 | 1.0-SNAPSHOT 7 | 8 | 9 | 2.12 10 | 3.2.3.RELEASE 11 | 1.3.8 12 | 2.2.3 13 | 1.7.7 14 | 9.2.3.v20140905 15 | 16 | 17 | 18 | SpringWithSwagger 19 | 20 | 21 | maven-compiler-plugin 22 | 23 | 1.7 24 | 1.7 25 | 26 | 27 | 28 | org.eclipse.jetty 29 | jetty-maven-plugin 30 | ${jetty.version} 31 | 32 | 33 | /SpringWithSwagger 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | org.glassfish.jersey.core 44 | jersey-server 45 | ${jersey.version} 46 | 47 | 48 | org.glassfish.jersey.containers 49 | jersey-container-servlet 50 | ${jersey.version} 51 | 52 | 53 | org.glassfish.jersey.media 54 | jersey-media-multipart 55 | ${jersey.version} 56 | 57 | 58 | org.glassfish.jersey.ext 59 | jersey-spring3 60 | ${jersey.version} 61 | 62 | 63 | spring-context 64 | org.springframework 65 | 66 | 67 | spring-core 68 | org.springframework 69 | 70 | 71 | 72 | 73 | org.glassfish.jersey.media 74 | jersey-media-json-jackson 75 | ${jersey.version} 76 | 77 | 78 | 79 | org.springframework 80 | spring-core 81 | ${spring.version} 82 | 83 | 84 | org.springframework 85 | spring-context 86 | ${spring.version} 87 | 88 | 89 | 90 | com.wordnik 91 | swagger-jersey2-jaxrs_2.10 92 | ${swagger-version} 93 | 94 | 95 | 96 | org.slf4j 97 | slf4j-api 98 | ${log4j.version} 99 | 100 | 101 | org.slf4j 102 | slf4j-log4j12 103 | ${log4j.version} 104 | 105 | 106 | 107 | org.mortbay.jetty 108 | servlet-api 109 | 3.0.20100224 110 | provided 111 | 112 | 113 | com.google.guava 114 | guava 115 | 18.0 116 | 117 | 118 | commons-io 119 | commons-io 120 | 2.4 121 | 122 | 123 | commons-validator 124 | commons-validator 125 | 1.3.1 126 | 127 | 128 | oro 129 | oro 130 | 2.0.8 131 | 132 | 133 | -------------------------------------------------------------------------------- /src/main/java/com/jakubstas/swagger/SpringWithSwagger.java: -------------------------------------------------------------------------------- 1 | package com.jakubstas.swagger; 2 | 3 | import javax.ws.rs.ApplicationPath; 4 | 5 | import org.glassfish.jersey.media.multipart.MultiPartFeature; 6 | import org.glassfish.jersey.server.ResourceConfig; 7 | 8 | @ApplicationPath("resources") 9 | public class SpringWithSwagger extends ResourceConfig { 10 | 11 | public SpringWithSwagger() { 12 | final String myRestPackage = "com.jakubstas.swagger.rest"; 13 | final String jacksonPackage = "org.codehaus.jackson.jaxrs"; 14 | 15 | final String swaggerJaxrsJsonPackage = "com.wordnik.swagger.jaxrs.json"; 16 | final String swaggerJaxrsListingPackage = "com.wordnik.swagger.jaxrs.listing"; 17 | 18 | packages(swaggerJaxrsJsonPackage, swaggerJaxrsListingPackage, jacksonPackage, myRestPackage); 19 | 20 | // enable multipart 21 | register(MultiPartFeature.class); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/jakubstas/swagger/model/Employee.java: -------------------------------------------------------------------------------- 1 | package com.jakubstas.swagger.model; 2 | 3 | import com.wordnik.swagger.annotations.ApiModel; 4 | import com.wordnik.swagger.annotations.ApiModelProperty; 5 | 6 | @ApiModel 7 | public class Employee { 8 | 9 | private String firstName; 10 | 11 | private String surname; 12 | 13 | private int employeeNumber; 14 | 15 | @ApiModelProperty(required = true) 16 | public String getFirstName() { 17 | return firstName; 18 | } 19 | 20 | public void setFirstName(String firstName) { 21 | this.firstName = firstName; 22 | } 23 | 24 | @ApiModelProperty(required = true) 25 | public String getSurname() { 26 | return surname; 27 | } 28 | 29 | public void setSurname(String surname) { 30 | this.surname = surname; 31 | } 32 | 33 | @ApiModelProperty(required = true) 34 | public int getEmployeeNumber() { 35 | return employeeNumber; 36 | } 37 | 38 | public void setEmployeeNumber(int employeeNumber) { 39 | this.employeeNumber = employeeNumber; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/jakubstas/swagger/model/Product.java: -------------------------------------------------------------------------------- 1 | package com.jakubstas.swagger.model; 2 | 3 | import javax.xml.bind.annotation.XmlAccessType; 4 | import javax.xml.bind.annotation.XmlAccessorType; 5 | import javax.xml.bind.annotation.XmlElement; 6 | import javax.xml.bind.annotation.XmlRootElement; 7 | 8 | import com.wordnik.swagger.annotations.ApiModel; 9 | import com.wordnik.swagger.annotations.ApiModelProperty; 10 | 11 | @ApiModel 12 | @XmlRootElement(name = "product", namespace = "com.jakubstas.swagger") 13 | @XmlAccessorType(XmlAccessType.NONE) 14 | public class Product { 15 | 16 | private String name; 17 | 18 | private String code; 19 | 20 | private String description; 21 | 22 | private final String oneToBeHidden = "hiddenOne"; 23 | 24 | @ApiModelProperty(required = true) 25 | @XmlElement(name = "name", nillable = false, required = true) 26 | public String getName() { 27 | return name; 28 | } 29 | 30 | public void setName(String name) { 31 | this.name = name; 32 | } 33 | 34 | @ApiModelProperty(required = true) 35 | @XmlElement(name = "code", nillable = false, required = true) 36 | public String getCode() { 37 | return code; 38 | } 39 | 40 | public void setCode(String code) { 41 | this.code = code; 42 | } 43 | 44 | @ApiModelProperty(required = true) 45 | @XmlElement(name = "description", nillable = false, required = true) 46 | public String getDescription() { 47 | return description; 48 | } 49 | 50 | public void setDescription(String description) { 51 | this.description = description; 52 | } 53 | 54 | @ApiModelProperty(access = "hidden") 55 | public String getOneToBeHidden() { 56 | return oneToBeHidden; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/jakubstas/swagger/model/ProductList.java: -------------------------------------------------------------------------------- 1 | package com.jakubstas.swagger.model; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collection; 5 | import java.util.List; 6 | 7 | import javax.xml.bind.annotation.XmlAttribute; 8 | import javax.xml.bind.annotation.XmlElement; 9 | import javax.xml.bind.annotation.XmlRootElement; 10 | 11 | import com.wordnik.swagger.annotations.ApiModel; 12 | import com.wordnik.swagger.annotations.ApiModelProperty; 13 | 14 | @ApiModel 15 | @XmlRootElement(name = "productList", namespace = "com.jakubstas.swagger") 16 | public class ProductList { 17 | private List products; 18 | 19 | public ProductList() { 20 | } 21 | 22 | public ProductList(final Collection products) { 23 | this.products = new ArrayList<>(products); 24 | } 25 | 26 | @ApiModelProperty(required = true, position = 1) 27 | @XmlElement(name = "product", required = true) 28 | public List getProducts() { 29 | return products; 30 | } 31 | 32 | public void setProducts(List products) { 33 | this.products = products; 34 | } 35 | 36 | @ApiModelProperty(required = true, position = 2) 37 | @XmlAttribute(name = "size", namespace = "com.jakubstas.swagger", required = true) 38 | public int getCount() { 39 | return products.size(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/jakubstas/swagger/model/User.java: -------------------------------------------------------------------------------- 1 | package com.jakubstas.swagger.model; 2 | 3 | import java.util.Date; 4 | 5 | import com.fasterxml.jackson.annotation.JsonIgnore; 6 | import com.wordnik.swagger.annotations.ApiModel; 7 | import com.wordnik.swagger.annotations.ApiModelProperty; 8 | 9 | @ApiModel 10 | public class User { 11 | 12 | private String userName; 13 | 14 | private String firstName; 15 | 16 | private String surname; 17 | 18 | private String email; 19 | 20 | private byte[] avatar; 21 | 22 | private Date lastUpdated; 23 | 24 | private final String oneToBeHidden = "hiddenOne"; 25 | 26 | @ApiModelProperty(position = 1, required = true, value = "username containing only lowercase letters or numbers") 27 | public String getUserName() { 28 | return userName; 29 | } 30 | 31 | public void setUserName(String userName) { 32 | this.userName = userName; 33 | } 34 | 35 | @ApiModelProperty(position = 2, required = true) 36 | public String getFirstName() { 37 | return firstName; 38 | } 39 | 40 | public void setFirstName(String firstName) { 41 | this.firstName = firstName; 42 | } 43 | 44 | @ApiModelProperty(position = 3, required = true) 45 | public String getSurname() { 46 | return surname; 47 | } 48 | 49 | public void setSurname(String surname) { 50 | this.surname = surname; 51 | } 52 | 53 | @ApiModelProperty(position = 4, required = true) 54 | public String getEmail() { 55 | return email; 56 | } 57 | 58 | public void setEmail(String email) { 59 | this.email = email; 60 | } 61 | 62 | @JsonIgnore 63 | public byte[] getAvatar() { 64 | return avatar; 65 | } 66 | 67 | public void setAvatar(byte[] avatar) { 68 | this.avatar = avatar; 69 | } 70 | 71 | @ApiModelProperty(position = 5, value = "timestamp of last modification") 72 | public Date getLastUpdated() { 73 | return lastUpdated; 74 | } 75 | 76 | public void setLastUpdated(Date lastUpdated) { 77 | this.lastUpdated = lastUpdated; 78 | } 79 | 80 | @ApiModelProperty(access = "hidden") 81 | public String getOneToBeHidden() { 82 | return oneToBeHidden; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/com/jakubstas/swagger/model/UserList.java: -------------------------------------------------------------------------------- 1 | package com.jakubstas.swagger.model; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collection; 5 | import java.util.List; 6 | 7 | import com.wordnik.swagger.annotations.ApiModel; 8 | import com.wordnik.swagger.annotations.ApiModelProperty; 9 | 10 | @ApiModel 11 | public class UserList { 12 | 13 | private List users = new ArrayList(); 14 | 15 | public UserList() { 16 | } 17 | 18 | public UserList(final Collection users) { 19 | this.users.addAll(users); 20 | } 21 | 22 | @ApiModelProperty(required = true, position = 1) 23 | public List getUsers() { 24 | return users; 25 | } 26 | 27 | @ApiModelProperty(required = true, position = 2) 28 | public int getCount() { 29 | return users.size(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/jakubstas/swagger/rest/EmployeeEndpoint.java: -------------------------------------------------------------------------------- 1 | package com.jakubstas.swagger.rest; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import javax.ws.rs.GET; 7 | import javax.ws.rs.OPTIONS; 8 | import javax.ws.rs.Path; 9 | import javax.ws.rs.Produces; 10 | import javax.ws.rs.core.MediaType; 11 | import javax.ws.rs.core.Response; 12 | 13 | import org.springframework.web.bind.annotation.RequestMethod; 14 | 15 | import com.google.common.base.Joiner; 16 | import com.google.common.net.HttpHeaders; 17 | import com.jakubstas.swagger.model.Employee; 18 | import com.wordnik.swagger.annotations.Api; 19 | import com.wordnik.swagger.annotations.ApiOperation; 20 | import com.wordnik.swagger.annotations.ApiResponse; 21 | import com.wordnik.swagger.annotations.ApiResponses; 22 | 23 | @Api(value = "/employees", description = "Endpoint for employee listing") 24 | @Path("/employees") 25 | public class EmployeeEndpoint { 26 | 27 | private List employees = new ArrayList(); 28 | 29 | { 30 | final Employee employee = new Employee(); 31 | employee.setEmployeeNumber(1); 32 | employee.setFirstName("Jakub"); 33 | employee.setSurname("Stas"); 34 | 35 | employees.add(employee); 36 | } 37 | 38 | @OPTIONS 39 | @ApiOperation( 40 | value = "Returns resource options", 41 | notes = "This method allows the client to determine the options and/or requirements associated with a resource, or the capabilities of a server, without implying a resource action or initiating a resource retrieval.") 42 | @ApiResponses(value = { @ApiResponse(code = 200, message = "Successful retrieval of resource options"), @ApiResponse(code = 500, message = "Internal server error") }) 43 | public Response getProductsOptions() { 44 | final String header = HttpHeaders.ALLOW; 45 | final String value = Joiner.on(", ").join(RequestMethod.GET, RequestMethod.OPTIONS).toString(); 46 | 47 | return Response.noContent().header(header, value).build(); 48 | } 49 | 50 | @GET 51 | @Produces(MediaType.APPLICATION_JSON) 52 | @ApiOperation(value = "Returns all employees", notes = "Returns a complete list of employees.", responseContainer = "array", response = Employee.class) 53 | @ApiResponses(value = { @ApiResponse(code = 200, message = "Successful retrieval of all employees", response = Employee.class), @ApiResponse(code = 500, message = "Internal server error") }) 54 | public Response getEmployees() { 55 | return Response.ok(employees).build(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/jakubstas/swagger/rest/ProductsEndpoint.java: -------------------------------------------------------------------------------- 1 | package com.jakubstas.swagger.rest; 2 | 3 | import java.net.URI; 4 | 5 | import javax.inject.Inject; 6 | import javax.ws.rs.Consumes; 7 | import javax.ws.rs.DELETE; 8 | import javax.ws.rs.GET; 9 | import javax.ws.rs.OPTIONS; 10 | import javax.ws.rs.POST; 11 | import javax.ws.rs.PUT; 12 | import javax.ws.rs.Path; 13 | import javax.ws.rs.PathParam; 14 | import javax.ws.rs.Produces; 15 | import javax.ws.rs.core.Context; 16 | import javax.ws.rs.core.Link; 17 | import javax.ws.rs.core.MediaType; 18 | import javax.ws.rs.core.Response; 19 | import javax.ws.rs.core.Response.Status; 20 | import javax.ws.rs.core.UriBuilder; 21 | import javax.ws.rs.core.UriInfo; 22 | 23 | import org.springframework.web.bind.annotation.RequestMethod; 24 | 25 | import com.google.common.base.Joiner; 26 | import com.google.common.net.HttpHeaders; 27 | import com.jakubstas.swagger.model.Product; 28 | import com.jakubstas.swagger.model.ProductList; 29 | import com.jakubstas.swagger.service.EntityAlreadyExistsException; 30 | import com.jakubstas.swagger.service.EntityNotFoundException; 31 | import com.jakubstas.swagger.service.ProductService; 32 | import com.wordnik.swagger.annotations.Api; 33 | import com.wordnik.swagger.annotations.ApiOperation; 34 | import com.wordnik.swagger.annotations.ApiParam; 35 | import com.wordnik.swagger.annotations.ApiResponse; 36 | import com.wordnik.swagger.annotations.ApiResponses; 37 | 38 | @Api(value = "/products", description = "Endpoint for product management") 39 | @Path("/products") 40 | public class ProductsEndpoint { 41 | 42 | @Inject 43 | private ProductService productService; 44 | 45 | @Context 46 | private UriInfo uriInfo; 47 | 48 | @OPTIONS 49 | @ApiOperation( 50 | value = "Returns resource options", 51 | notes = "This method allows the client to determine the options and/or requirements associated with a resource, or the capabilities of a server, without implying a resource action or initiating a resource retrieval.") 52 | @ApiResponses(value = { @ApiResponse(code = 200, message = "Successful retrieval of resource options"), @ApiResponse(code = 500, message = "Internal server error") }) 53 | public Response getProductsOptions() { 54 | final String header = HttpHeaders.ALLOW; 55 | final String value = Joiner.on(", ").join(RequestMethod.GET, RequestMethod.POST, RequestMethod.OPTIONS).toString(); 56 | 57 | return Response.noContent().header(header, value).build(); 58 | } 59 | 60 | @GET 61 | @Produces(MediaType.APPLICATION_XML) 62 | @ApiOperation(value = "Returns all products", notes = "Returns a complete list of products from catalog.") 63 | @ApiResponses(value = { @ApiResponse(code = 200, message = "Successful retrieval of all products"), @ApiResponse(code = 500, message = "Internal server error") }) 64 | public Response getProducts() { 65 | return Response.ok(new ProductList(productService.getAll())).build(); 66 | } 67 | 68 | @POST 69 | @Consumes(MediaType.APPLICATION_XML) 70 | @Produces(MediaType.APPLICATION_XML) 71 | @ApiOperation(value = "Creates a product", notes = "Creates a product and puts it in the catalog.") 72 | @ApiResponses(value = { @ApiResponse(code = 201, message = "Successful catalogization of new product"), @ApiResponse(code = 406, message = "Malformed definition of new product"), 73 | @ApiResponse(code = 409, message = "Product with specified code already exists"), @ApiResponse(code = 500, message = "Internal server error") }) 74 | public Response createProduct(@ApiParam(name = "product", required = true) Product product) { 75 | try { 76 | final Product newProduct = productService.createProduct(product); 77 | 78 | return Response.status(Status.CREATED).entity(newProduct).location(getLocation(newProduct)).links(getProductLinks(newProduct.getCode())).build(); 79 | } catch (EntityAlreadyExistsException e) { 80 | return Response.status(Status.CONFLICT).entity("Specified productCode is already taken.").build(); 81 | } catch (IllegalArgumentException e) { 82 | return Response.status(Status.NOT_ACCEPTABLE).entity(e.getMessage()).build(); 83 | } 84 | } 85 | 86 | @OPTIONS 87 | @Path("/{productCode}") 88 | @ApiOperation( 89 | value = "Returns resource options", 90 | notes = "This method allows the client to determine the options and/or requirements associated with a resource, or the capabilities of a server, without implying a resource action or initiating a resource retrieval.") 91 | @ApiResponses(value = { @ApiResponse(code = 200, message = "Successful retrieval of resource options"), @ApiResponse(code = 500, message = "Internal server error") }) 92 | public Response getProductOptions(@ApiParam("product identifier") @PathParam("productCode") String productCode) { 93 | final String header = HttpHeaders.ALLOW; 94 | final String value = Joiner.on(", ").join(RequestMethod.GET, RequestMethod.PUT, RequestMethod.DELETE, RequestMethod.OPTIONS).toString(); 95 | 96 | return Response.ok().header(header, value).build(); 97 | } 98 | 99 | @GET 100 | @Path("/{productCode}") 101 | @Produces(MediaType.APPLICATION_XML) 102 | @ApiOperation(value = "Returns product details", notes = "This method provides detailed product description.") 103 | @ApiResponses(value = { @ApiResponse(code = 200, message = "Successful retrieval of product details"), @ApiResponse(code = 404, message = "Product with given code does not exists"), 104 | @ApiResponse(code = 500, message = "Internal server error") }) 105 | public Response getProduct(@ApiParam("product identifier") @PathParam("productCode") String productCode) { 106 | final Product product = productService.findByCode(productCode); 107 | 108 | if (product == null) { 109 | return Response.status(Status.NOT_FOUND).entity("Product with given code does not exists").links(Link.fromUri("products").rel("create").type(MediaType.APPLICATION_XML).build(productCode)).build(); 110 | } else { 111 | return Response.status(Status.OK).entity(product).links(getProductLinks(productCode)).build(); 112 | } 113 | } 114 | 115 | @PUT 116 | @Path("/{productCode}") 117 | @Consumes(MediaType.APPLICATION_XML) 118 | @Produces(MediaType.APPLICATION_XML) 119 | @ApiOperation(value = "Updates product details", notes = "Updates a product from the catalog.") 120 | @ApiResponses(value = { @ApiResponse(code = 200, message = "Successful update of product details"), @ApiResponse(code = 404, message = "Product with given code does not exists"), 121 | @ApiResponse(code = 500, message = "Internal server error") }) 122 | public Response updateProduct(@ApiParam("product identifier") @PathParam("productCode") String productCode, @ApiParam(name = "product", required = true) Product product) { 123 | try { 124 | final Product updatedProduct = productService.updateProduct(productCode, product); 125 | 126 | return Response.status(Status.OK).entity(updatedProduct).links(getProductLinks(productCode)).build(); 127 | } catch (EntityNotFoundException e) { 128 | return Response.status(Status.NOT_FOUND).entity("Product with given code does not exists").links(Link.fromUri("products").rel("create").type(MediaType.APPLICATION_XML).build()).build(); 129 | } catch (IllegalArgumentException e) { 130 | return Response.status(Status.NOT_ACCEPTABLE).entity(e.getMessage()).build(); 131 | } 132 | } 133 | 134 | @DELETE 135 | @Path("/{productCode}") 136 | @Produces(MediaType.APPLICATION_XML) 137 | @ApiOperation(value = "Deletes a product", notes = "Deletes a product and removes it from the catalog.") 138 | @ApiResponses(value = { @ApiResponse(code = 200, message = "Successful removal of product details"), @ApiResponse(code = 404, message = "Product with given code does not exists"), 139 | @ApiResponse(code = 500, message = "Internal server error") }) 140 | public Response deleteProduct(@ApiParam("product identifier") @PathParam("productCode") String productCode) { 141 | try { 142 | final Product deletedProduct = productService.deleteProduct(productCode); 143 | 144 | return Response.status(Status.OK).entity(deletedProduct).build(); 145 | } catch (EntityNotFoundException e) { 146 | return Response.status(Status.NOT_FOUND).entity("Product with given code does not exists").links(Link.fromUri("products").rel("create").type(MediaType.APPLICATION_XML).build()).build(); 147 | } 148 | } 149 | 150 | private Link[] getProductLinks(final String productCode) { 151 | final Link[] links = new Link[2]; 152 | 153 | links[0] = Link.fromUri("products/{productCode}").rel("update").type(MediaType.APPLICATION_XML).build(productCode); 154 | links[1] = Link.fromUri("products").rel("listAll").type(MediaType.APPLICATION_XML).build(productCode); 155 | 156 | return links; 157 | } 158 | 159 | private URI getLocation(final Product product) { 160 | return UriBuilder.fromUri("products/{productCode}").build(product.getCode()); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/main/java/com/jakubstas/swagger/rest/UsersEndpoint.java: -------------------------------------------------------------------------------- 1 | package com.jakubstas.swagger.rest; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.net.URI; 6 | 7 | import javax.inject.Inject; 8 | import javax.ws.rs.Consumes; 9 | import javax.ws.rs.GET; 10 | import javax.ws.rs.OPTIONS; 11 | import javax.ws.rs.POST; 12 | import javax.ws.rs.PUT; 13 | import javax.ws.rs.Path; 14 | import javax.ws.rs.PathParam; 15 | import javax.ws.rs.Produces; 16 | import javax.ws.rs.core.Context; 17 | import javax.ws.rs.core.Link; 18 | import javax.ws.rs.core.MediaType; 19 | import javax.ws.rs.core.Response; 20 | import javax.ws.rs.core.Response.Status; 21 | import javax.ws.rs.core.UriBuilder; 22 | import javax.ws.rs.core.UriInfo; 23 | 24 | import org.glassfish.jersey.media.multipart.FormDataParam; 25 | import org.springframework.web.bind.annotation.RequestMethod; 26 | 27 | import com.google.common.base.Joiner; 28 | import com.google.common.net.HttpHeaders; 29 | import com.jakubstas.swagger.model.User; 30 | import com.jakubstas.swagger.model.UserList; 31 | import com.jakubstas.swagger.service.EntityAlreadyExistsException; 32 | import com.jakubstas.swagger.service.EntityNotFoundException; 33 | import com.jakubstas.swagger.service.UserService; 34 | import com.wordnik.swagger.annotations.Api; 35 | import com.wordnik.swagger.annotations.ApiImplicitParam; 36 | import com.wordnik.swagger.annotations.ApiImplicitParams; 37 | import com.wordnik.swagger.annotations.ApiOperation; 38 | import com.wordnik.swagger.annotations.ApiParam; 39 | import com.wordnik.swagger.annotations.ApiResponse; 40 | import com.wordnik.swagger.annotations.ApiResponses; 41 | 42 | /** 43 | * REST endpoint for user manipulation. 44 | */ 45 | @Api(value = "users", description = "Endpoint for user management") 46 | @Path("/users") 47 | public class UsersEndpoint { 48 | 49 | @Inject 50 | private UserService userService; 51 | 52 | @Context 53 | private UriInfo uriInfo; 54 | 55 | @OPTIONS 56 | @ApiOperation( 57 | value = "Returns resource options", 58 | notes = "This method allows the client to determine the options and/or requirements associated with a resource, or the capabilities of a server, without implying a resource action or initiating a resource retrieval.") 59 | @ApiResponses(value = { @ApiResponse(code = 200, message = "Successful retrieval of resource options"), @ApiResponse(code = 500, message = "Internal server error") }) 60 | public Response getUsersOptions() { 61 | final String header = HttpHeaders.ALLOW; 62 | final String value = Joiner.on(", ").join(RequestMethod.GET, RequestMethod.POST, RequestMethod.OPTIONS).toString(); 63 | 64 | return Response.noContent().header(header, value).build(); 65 | } 66 | 67 | /** 68 | * Returns a complete list of users registered within application. 69 | */ 70 | @GET 71 | @Produces(MediaType.APPLICATION_JSON) 72 | @ApiOperation(value = "Returns all users", notes = "Returns a complete list of users registered within application.", response = UserList.class) 73 | @ApiResponses(value = { @ApiResponse(code = 200, message = "Successful retrieval of all users", response = UserList.class), @ApiResponse(code = 500, message = "Internal server error") }) 74 | public Response getUsers() { 75 | final UserList users = new UserList(userService.getAll()); 76 | 77 | return Response.ok(users, MediaType.APPLICATION_JSON_TYPE).build(); 78 | } 79 | 80 | /** 81 | * Creates and registers user in application. 82 | */ 83 | @POST 84 | @Consumes(MediaType.APPLICATION_JSON) 85 | @Produces(MediaType.APPLICATION_JSON) 86 | @ApiOperation(value = "Creates single user", notes = "Creates and registers user in application.", response = User.class) 87 | @ApiResponses(value = { @ApiResponse(code = 201, message = "Successful registration of new user", response = User.class), @ApiResponse(code = 406, message = "Malformed definition of new user"), 88 | @ApiResponse(code = 409, message = "User with specified username already exists"), @ApiResponse(code = 500, message = "Internal server error") }) 89 | public Response createUser(@ApiParam(name = "user", required = true) User user) { 90 | try { 91 | final User newUser = userService.createUser(user); 92 | 93 | return Response.status(Status.CREATED).entity(newUser).location(getLocation(newUser)).links(getUserLinks(newUser)).build(); 94 | } catch (EntityAlreadyExistsException e) { 95 | return Response.status(Status.CONFLICT).entity("Specified username is already taken.").build(); 96 | } catch (IllegalArgumentException e) { 97 | return Response.status(Status.NOT_ACCEPTABLE).entity(e.getMessage()).build(); 98 | } 99 | } 100 | 101 | @OPTIONS 102 | @Path("/{userName}") 103 | @ApiOperation( 104 | value = "Returns resource options", 105 | notes = "This method allows the client to determine the options and/or requirements associated with a resource, or the capabilities of a server, without implying a resource action or initiating a resource retrieval.") 106 | @ApiResponses(value = { @ApiResponse(code = 200, message = "Successful retrieval of resource options"), @ApiResponse(code = 500, message = "Internal server error") }) 107 | public Response getUserOptions() { 108 | final String header = HttpHeaders.ALLOW; 109 | final String value = Joiner.on(", ").join(RequestMethod.GET, RequestMethod.OPTIONS).toString(); 110 | 111 | return Response.noContent().header(header, value).build(); 112 | } 113 | 114 | @GET 115 | @Path("/{userName}") 116 | @Produces(MediaType.APPLICATION_JSON) 117 | @ApiOperation(value = "Returns user details", notes = "Returns a complete list of users details with a date of last modification.", response = User.class) 118 | @ApiResponses(value = { @ApiResponse(code = 200, message = "Successful retrieval of user detail", response = User.class), 119 | @ApiResponse(code = 404, message = "User with given username does not exist"), @ApiResponse(code = 500, message = "Internal server error") }) 120 | public Response getUser(@ApiParam(name = "userName", value = "Alphanumeric login to application", required = true) @PathParam("userName") String userName) { 121 | final User user = userService.findByUserName(userName); 122 | 123 | if (user != null) { 124 | return Response.status(Status.OK).entity(user).build(); 125 | } else { 126 | return Response.status(Status.NOT_FOUND).entity("User with specified username does not exist.").build(); 127 | } 128 | } 129 | 130 | @OPTIONS 131 | @Path("/{userName}/avatar") 132 | @ApiOperation( 133 | value = "Returns resource options", 134 | notes = "This method allows the client to determine the options and/or requirements associated with a resource, or the capabilities of a server, without implying a resource action or initiating a resource retrieval.") 135 | @ApiResponses(value = { @ApiResponse(code = 200, message = "Successful retrieval of resource options"), @ApiResponse(code = 500, message = "Internal server error") }) 136 | public Response getAvatarOptions() { 137 | final String header = HttpHeaders.ALLOW; 138 | final String value = Joiner.on(", ").join(RequestMethod.GET, RequestMethod.OPTIONS).toString(); 139 | 140 | return Response.noContent().header(header, value).build(); 141 | } 142 | 143 | /** 144 | * Updates users avatar. 145 | */ 146 | @GET 147 | @Path("/{userName}/avatar") 148 | @Produces("image/png") 149 | @ApiOperation(value = "Returns users avatar", notes = "Provides means to download avatar based on username") 150 | @ApiResponses(value = { @ApiResponse(code = 200, message = "Successful retrieval of users avatar"), 151 | @ApiResponse(code = 404, message = "User with given username does not exist"), @ApiResponse(code = 500, message = "Internal server error") }) 152 | public Response getUsersAvatar(@ApiParam(name = "userName", value = "Alphanumeric login to application", required = true) @PathParam("userName") String userName) { 153 | final User user = userService.findByUserName(userName); 154 | 155 | if (user != null) { 156 | return Response.status(Status.OK).entity(user.getAvatar()).build(); 157 | } else { 158 | return Response.status(Status.NOT_FOUND).entity("User with specified username does not exist.").build(); 159 | } 160 | } 161 | 162 | /** 163 | * Updates users avatar. 164 | */ 165 | @PUT 166 | @Path("/{userName}/avatar") 167 | @Consumes(MediaType.MULTIPART_FORM_DATA) 168 | @ApiImplicitParams(@ApiImplicitParam(dataType = "file", name = "avatar", paramType = "body")) 169 | @ApiOperation(value = "Updates users avatar", notes = "Provides means to upload new versions of avatar based on username") 170 | @ApiResponses(value = { @ApiResponse(code = 200, message = "Successful retrieval of users avatar"), 171 | @ApiResponse(code = 404, message = "User with given username does not exist"), @ApiResponse(code = 500, message = "Internal server error") }) 172 | public Response updateUsersAvatar(@ApiParam(name = "userName", value = "Alphanumeric login to application", required = true) @PathParam("userName") String userName, 173 | @ApiParam(access = "hidden") @FormDataParam("avatar") InputStream avatarInputStream) { 174 | try { 175 | final User user = userService.updateAvatar(userName, avatarInputStream); 176 | 177 | return Response.status(Status.OK).location(getAvatarLocation(user)).build(); 178 | } catch (EntityNotFoundException e) { 179 | return Response.status(Status.NOT_FOUND).entity("User with specified username does not exist.").build(); 180 | } catch (IOException e) { 181 | return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Internal server error.").build(); 182 | } 183 | } 184 | 185 | private Link[] getUserLinks(final User user) { 186 | final Link[] links = new Link[2]; 187 | 188 | links[0] = Link.fromUri("users/{userName}").rel("update").type(MediaType.APPLICATION_XML).build(user.getUserName()); 189 | links[1] = Link.fromUri("users").rel("listAll").type(MediaType.APPLICATION_XML).build(); 190 | 191 | return links; 192 | } 193 | 194 | private URI getLocation(final User user) { 195 | return UriBuilder.fromUri("users/{userName}").build(user.getUserName()); 196 | } 197 | 198 | private URI getAvatarLocation(final User user) { 199 | return UriBuilder.fromUri("users/{userName}/avatar").build(user.getUserName()); 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/main/java/com/jakubstas/swagger/rest/config/AccessHiddenModelConverter.java: -------------------------------------------------------------------------------- 1 | package com.jakubstas.swagger.rest.config; 2 | 3 | import java.beans.IntrospectionException; 4 | import java.beans.Introspector; 5 | import java.beans.PropertyDescriptor; 6 | import java.lang.reflect.Method; 7 | 8 | import javax.xml.bind.annotation.XmlElement; 9 | 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | import org.springframework.util.StringUtils; 13 | 14 | import scala.Option; 15 | import scala.collection.immutable.Map; 16 | 17 | import com.wordnik.swagger.annotations.ApiModelProperty; 18 | import com.wordnik.swagger.converter.SwaggerSchemaConverter; 19 | import com.wordnik.swagger.model.Model; 20 | 21 | /** 22 | * API model schema filter to enable hiding of API model attributes. 23 | */ 24 | public class AccessHiddenModelConverter extends SwaggerSchemaConverter { 25 | 26 | private final Logger log = LoggerFactory.getLogger(AccessHiddenModelConverter.class); 27 | 28 | @Override 29 | public Option read(final Class modelClass, final Map typeMap) { 30 | final Option modelOption = super.read(modelClass, typeMap); 31 | 32 | Class currentClass = modelClass; 33 | 34 | while (currentClass.getSuperclass() != null) { 35 | for (final Method method : modelClass.getDeclaredMethods()) { 36 | if (method.isAnnotationPresent(ApiModelProperty.class) && isAccessHidden(method)) { 37 | hideModelProperty(currentClass, method, modelOption); 38 | } 39 | } 40 | currentClass = currentClass.getSuperclass(); 41 | } 42 | return modelOption; 43 | } 44 | 45 | private boolean isAccessHidden(final Method method) { 46 | if (!method.getAnnotation(ApiModelProperty.class).access().isEmpty()) { 47 | return method.getAnnotation(ApiModelProperty.class).access().equals("hidden"); 48 | } 49 | 50 | return false; 51 | } 52 | 53 | private void hideModelProperty(final Class currentClass, final Method method, final Option modelOption) { 54 | final String propertyName; 55 | 56 | if (method.isAnnotationPresent(XmlElement.class) && !method.getAnnotation(XmlElement.class).name().isEmpty()) { 57 | propertyName = method.getAnnotation(XmlElement.class).name(); 58 | } else { 59 | propertyName = getClassMemberName(currentClass, method); 60 | } 61 | 62 | if (StringUtils.hasText(propertyName)) { 63 | modelOption.get().properties().remove(propertyName); 64 | log.debug("Successfully hidden API model property '" + propertyName + "'"); 65 | } 66 | } 67 | 68 | private String getClassMemberName(final Class currentClass, final Method method) { 69 | try { 70 | for (PropertyDescriptor propertyDescriptor : Introspector.getBeanInfo(currentClass).getPropertyDescriptors()) { 71 | if (propertyDescriptor.getReadMethod().getName().equals(method.getName())) { 72 | return propertyDescriptor.getName(); 73 | } 74 | } 75 | } catch (IntrospectionException e) { 76 | log.error("Unable to retrieve field name", e); 77 | } 78 | 79 | return null; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/jakubstas/swagger/rest/config/AccessHiddenSpecFilter.java: -------------------------------------------------------------------------------- 1 | package com.jakubstas.swagger.rest.config; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | import com.wordnik.swagger.core.filter.SwaggerSpecFilter; 7 | import com.wordnik.swagger.model.ApiDescription; 8 | import com.wordnik.swagger.model.Operation; 9 | import com.wordnik.swagger.model.Parameter; 10 | 11 | /** 12 | * Specification filter to enable hiding of API parameters. 13 | */ 14 | public class AccessHiddenSpecFilter implements SwaggerSpecFilter { 15 | 16 | @Override 17 | public boolean isOperationAllowed(Operation arg0, ApiDescription arg1, Map> arg2, Map arg3, Map> arg4) { 18 | return true; 19 | } 20 | 21 | @Override 22 | public boolean isParamAllowed(Parameter param, Operation operation, ApiDescription desc, Map> arg3, Map arg4, Map> arg5) { 23 | final String paramAccess = param.paramAccess().toString(); 24 | 25 | return !paramAccess.equalsIgnoreCase("Some(hidden)"); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/jakubstas/swagger/rest/config/SwaggerConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.jakubstas.swagger.rest.config; 2 | 3 | import javax.annotation.PostConstruct; 4 | 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.stereotype.Component; 7 | 8 | import com.wordnik.swagger.config.ConfigFactory; 9 | import com.wordnik.swagger.config.FilterFactory; 10 | import com.wordnik.swagger.config.ScannerFactory; 11 | import com.wordnik.swagger.config.SwaggerConfig; 12 | import com.wordnik.swagger.converter.ModelConverters; 13 | import com.wordnik.swagger.jaxrs.config.ReflectiveJaxrsScanner; 14 | import com.wordnik.swagger.jaxrs.reader.DefaultJaxrsApiReader; 15 | import com.wordnik.swagger.reader.ClassReaders; 16 | 17 | /** 18 | * Configuration bean to set up Swagger. 19 | */ 20 | @Component 21 | public class SwaggerConfiguration { 22 | 23 | @Value("${swagger.resourcePackage}") 24 | private String resourcePackage; 25 | 26 | @Value("${swagger.basePath}") 27 | private String basePath; 28 | 29 | @Value("${swagger.apiVersion}") 30 | private String apiVersion; 31 | 32 | @PostConstruct 33 | public void init() { 34 | final ReflectiveJaxrsScanner scanner = new ReflectiveJaxrsScanner(); 35 | scanner.setResourcePackage(resourcePackage); 36 | 37 | ScannerFactory.setScanner(scanner); 38 | ClassReaders.setReader(new DefaultJaxrsApiReader()); 39 | ModelConverters.addConverter(new AccessHiddenModelConverter(), true); 40 | FilterFactory.setFilter(new AccessHiddenSpecFilter()); 41 | 42 | final SwaggerConfig config = ConfigFactory.config(); 43 | config.setApiVersion(apiVersion); 44 | config.setBasePath(basePath); 45 | } 46 | 47 | public String getResourcePackage() { 48 | return resourcePackage; 49 | } 50 | 51 | public void setResourcePackage(String resourcePackage) { 52 | this.resourcePackage = resourcePackage; 53 | } 54 | 55 | public String getBasePath() { 56 | return basePath; 57 | } 58 | 59 | public void setBasePath(String basePath) { 60 | this.basePath = basePath; 61 | } 62 | 63 | public String getApiVersion() { 64 | return apiVersion; 65 | } 66 | 67 | public void setApiVersion(String apiVersion) { 68 | this.apiVersion = apiVersion; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/jakubstas/swagger/service/EntityAlreadyExistsException.java: -------------------------------------------------------------------------------- 1 | package com.jakubstas.swagger.service; 2 | 3 | public class EntityAlreadyExistsException extends Exception { 4 | private static final long serialVersionUID = 4353599075280907473L; 5 | 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/com/jakubstas/swagger/service/EntityNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.jakubstas.swagger.service; 2 | 3 | public class EntityNotFoundException extends Exception { 4 | private static final long serialVersionUID = 823265040113361862L; 5 | 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/com/jakubstas/swagger/service/ProductService.java: -------------------------------------------------------------------------------- 1 | package com.jakubstas.swagger.service; 2 | 3 | import java.io.IOException; 4 | import java.util.Collection; 5 | import java.util.Map; 6 | import java.util.concurrent.ConcurrentHashMap; 7 | 8 | import javax.annotation.PostConstruct; 9 | 10 | import org.springframework.stereotype.Service; 11 | import org.springframework.util.StringUtils; 12 | 13 | import com.google.common.base.Preconditions; 14 | import com.jakubstas.swagger.model.Product; 15 | 16 | @Service 17 | public class ProductService { 18 | 19 | private final Map products = new ConcurrentHashMap(); 20 | 21 | @PostConstruct 22 | public void init() throws IOException { 23 | final Product product1 = new Product(); 24 | product1.setCode("prod1"); 25 | product1.setName("Soap"); 26 | product1.setDescription("a bar of soap"); 27 | 28 | final Product product2 = new Product(); 29 | product2.setCode("prod2"); 30 | product2.setName("Water"); 31 | product2.setDescription("a bottle of water"); 32 | 33 | products.put(product1.getCode(), product1); 34 | products.put(product2.getCode(), product2); 35 | } 36 | 37 | public Product createProduct(final Product product) throws EntityAlreadyExistsException { 38 | Preconditions.checkArgument(StringUtils.hasText(product.getCode()), "Invalid product definition! Missing product code."); 39 | Preconditions.checkArgument(StringUtils.hasText(product.getName()), "Invalid product definition! Missing name."); 40 | Preconditions.checkArgument(StringUtils.hasText(product.getDescription()), "Invalid product definition! Missing description."); 41 | 42 | if (products.containsKey(product.getCode())) { 43 | throw new EntityAlreadyExistsException(); 44 | } 45 | 46 | products.put(product.getCode(), product); 47 | 48 | return product; 49 | } 50 | 51 | public Product findByCode(final String code) { 52 | return products.get(code); 53 | } 54 | 55 | public Product updateProduct(final String productCode, final Product newProduct) throws EntityNotFoundException { 56 | Preconditions.checkArgument(StringUtils.hasText(newProduct.getCode()), "Invalid product definition! Missing product code."); 57 | Preconditions.checkArgument(productCode.equals(newProduct.getCode()), "Product code mismatch."); 58 | Preconditions.checkArgument(StringUtils.hasText(newProduct.getName()), "Invalid product definition! Missing name."); 59 | Preconditions.checkArgument(StringUtils.hasText(newProduct.getDescription()), "Invalid product definition! Missing description."); 60 | 61 | if (!products.containsKey(newProduct.getCode())) { 62 | throw new EntityNotFoundException(); 63 | } 64 | 65 | final Product product = products.get(newProduct.getCode()); 66 | product.setName(newProduct.getName()); 67 | product.setDescription(newProduct.getDescription()); 68 | 69 | return product; 70 | } 71 | 72 | public Product deleteProduct(final String code) throws EntityNotFoundException { 73 | if (!products.containsKey(code)) { 74 | throw new EntityNotFoundException(); 75 | } 76 | 77 | return products.remove(code); 78 | } 79 | 80 | public Collection getAll() { 81 | return products.values(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/jakubstas/swagger/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.jakubstas.swagger.service; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.nio.file.Files; 6 | import java.util.Collection; 7 | import java.util.Date; 8 | import java.util.Map; 9 | import java.util.concurrent.ConcurrentHashMap; 10 | import java.util.regex.Pattern; 11 | 12 | import javax.annotation.PostConstruct; 13 | 14 | import org.apache.commons.io.IOUtils; 15 | import org.apache.commons.validator.EmailValidator; 16 | import org.springframework.core.io.ClassPathResource; 17 | import org.springframework.core.io.Resource; 18 | import org.springframework.stereotype.Service; 19 | import org.springframework.util.StringUtils; 20 | 21 | import com.google.common.base.Preconditions; 22 | import com.jakubstas.swagger.model.User; 23 | 24 | @Service 25 | public class UserService { 26 | 27 | private final EmailValidator emailValidator = EmailValidator.getInstance(); 28 | 29 | private final Pattern userNamePattern = Pattern.compile("[^a-z0-9]"); 30 | 31 | private final Map users = new ConcurrentHashMap(); 32 | 33 | private final Resource avatarStan = new ClassPathResource("avatars/1.png"); 34 | 35 | private final Resource avatarKyle = new ClassPathResource("avatars/2.png"); 36 | 37 | @PostConstruct 38 | public void init() throws IOException { 39 | final byte[] avatarStanBytes = Files.readAllBytes(avatarStan.getFile().toPath()); 40 | final byte[] avatarKyleBytes = Files.readAllBytes(avatarKyle.getFile().toPath()); 41 | 42 | final User userStan = new User(); 43 | userStan.setUserName("stan"); 44 | userStan.setFirstName("Stanley"); 45 | userStan.setSurname("Marsh"); 46 | userStan.setEmail("stan@jakubstas.com"); 47 | userStan.setAvatar(avatarStanBytes); 48 | userStan.setLastUpdated(new Date()); 49 | 50 | final User userKyle = new User(); 51 | userKyle.setUserName("kyle"); 52 | userKyle.setFirstName("Kyle"); 53 | userKyle.setSurname("Broflovski"); 54 | userKyle.setEmail("kyle@jakubstas.com"); 55 | userKyle.setAvatar(avatarKyleBytes); 56 | userKyle.setLastUpdated(new Date()); 57 | 58 | users.put(userStan.getUserName(), userStan); 59 | users.put(userKyle.getUserName(), userKyle); 60 | } 61 | 62 | public User findByUserName(final String userName) { 63 | return users.get(userName); 64 | } 65 | 66 | public Collection getAll() { 67 | return users.values(); 68 | } 69 | 70 | public User createUser(final User user) throws EntityAlreadyExistsException { 71 | Preconditions.checkArgument(StringUtils.hasText(user.getUserName()), "Invalid user definition! Missing username."); 72 | Preconditions.checkArgument(StringUtils.hasText(user.getFirstName()), "Invalid user definition! Missing first name."); 73 | Preconditions.checkArgument(StringUtils.hasText(user.getSurname()), "Invalid user definition! Missing surname."); 74 | Preconditions.checkArgument(StringUtils.hasText(user.getEmail()), "Invalid user definition! Missing email address."); 75 | 76 | final boolean isInvalidUserName = userNamePattern.matcher(user.getUserName()).find(); 77 | final boolean isValidEmail = emailValidator.isValid(user.getEmail()); 78 | 79 | Preconditions.checkArgument(!isInvalidUserName, "Invalid user definition! Username must contain only letters and numbers."); 80 | Preconditions.checkArgument(isValidEmail, "Invalid user definition! Invalid format of email address."); 81 | 82 | if (users.containsKey(user.getUserName())) { 83 | throw new EntityAlreadyExistsException(); 84 | } 85 | 86 | user.setUserName(user.getUserName().toLowerCase()); 87 | user.setLastUpdated(new Date()); 88 | users.put(user.getUserName(), user); 89 | 90 | return user; 91 | } 92 | 93 | public User updateUser(final String userName, final String firstName, final String surname, final String email) throws EntityNotFoundException { 94 | final User user = users.get(userName); 95 | 96 | if (user == null) { 97 | throw new EntityNotFoundException(); 98 | } 99 | 100 | if (StringUtils.hasText(firstName)) { 101 | user.setFirstName(firstName); 102 | } 103 | 104 | if (StringUtils.hasText(surname)) { 105 | user.setFirstName(surname); 106 | } 107 | 108 | if (StringUtils.hasText(email)) { 109 | user.setFirstName(email); 110 | } 111 | 112 | user.setLastUpdated(new Date()); 113 | 114 | return user; 115 | } 116 | 117 | public User updateAvatar(final String userName, final InputStream avatarIs) throws EntityNotFoundException, IOException { 118 | final User user = users.get(userName); 119 | 120 | if (user == null) { 121 | throw new EntityNotFoundException(); 122 | } 123 | 124 | user.setAvatar(IOUtils.toByteArray(avatarIs)); 125 | 126 | return user; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/main/resources/avatars/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakubStas/SpringWithSwagger/96b9e77ed711bfb1991d9f16d93eac388a5648e3/src/main/resources/avatars/1.png -------------------------------------------------------------------------------- /src/main/resources/avatars/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakubStas/SpringWithSwagger/96b9e77ed711bfb1991d9f16d93eac388a5648e3/src/main/resources/avatars/2.png -------------------------------------------------------------------------------- /src/main/resources/beans.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=INFO, console 2 | 3 | log4j.appender.console=org.apache.log4j.ConsoleAppender 4 | log4j.appender.console.layout=org.apache.log4j.PatternLayout 5 | log4j.appender.console.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n -------------------------------------------------------------------------------- /src/main/resources/swagger.properties: -------------------------------------------------------------------------------- 1 | swagger.apiVersion=1.0 2 | swagger.basePath=http://localhost:8080/SpringWithSwagger/rest 3 | swagger.resourcePackage=com.jakubstas.swagger.rest -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 3 | Spring Jersey Swagger Example 4 | 5 | 6 | contextConfigLocation 7 | classpath:beans.xml 8 | 9 | 10 | 11 | org.springframework.web.context.ContextLoaderListener 12 | 13 | 14 | 15 | jersey-serlvet 16 | org.glassfish.jersey.servlet.ServletContainer 17 | 18 | javax.ws.rs.Application 19 | com.jakubstas.swagger.SpringWithSwagger 20 | 21 | 1 22 | 23 | 24 | 25 | jersey-serlvet 26 | /rest/* 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/main/webapp/apidocs/css/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ v2.0 | 20110126 */ 2 | html, 3 | body, 4 | div, 5 | span, 6 | applet, 7 | object, 8 | iframe, 9 | h1, 10 | h2, 11 | h3, 12 | h4, 13 | h5, 14 | h6, 15 | p, 16 | blockquote, 17 | pre, 18 | a, 19 | abbr, 20 | acronym, 21 | address, 22 | big, 23 | cite, 24 | code, 25 | del, 26 | dfn, 27 | em, 28 | img, 29 | ins, 30 | kbd, 31 | q, 32 | s, 33 | samp, 34 | small, 35 | strike, 36 | strong, 37 | sub, 38 | sup, 39 | tt, 40 | var, 41 | b, 42 | u, 43 | i, 44 | center, 45 | dl, 46 | dt, 47 | dd, 48 | ol, 49 | ul, 50 | li, 51 | fieldset, 52 | form, 53 | label, 54 | legend, 55 | table, 56 | caption, 57 | tbody, 58 | tfoot, 59 | thead, 60 | tr, 61 | th, 62 | td, 63 | article, 64 | aside, 65 | canvas, 66 | details, 67 | embed, 68 | figure, 69 | figcaption, 70 | footer, 71 | header, 72 | hgroup, 73 | menu, 74 | nav, 75 | output, 76 | ruby, 77 | section, 78 | summary, 79 | time, 80 | mark, 81 | audio, 82 | video { 83 | margin: 0; 84 | padding: 0; 85 | border: 0; 86 | font-size: 100%; 87 | font: inherit; 88 | vertical-align: baseline; 89 | } 90 | /* HTML5 display-role reset for older browsers */ 91 | article, 92 | aside, 93 | details, 94 | figcaption, 95 | figure, 96 | footer, 97 | header, 98 | hgroup, 99 | menu, 100 | nav, 101 | section { 102 | display: block; 103 | } 104 | body { 105 | line-height: 1; 106 | } 107 | ol, 108 | ul { 109 | list-style: none; 110 | } 111 | blockquote, 112 | q { 113 | quotes: none; 114 | } 115 | blockquote:before, 116 | blockquote:after, 117 | q:before, 118 | q:after { 119 | content: ''; 120 | content: none; 121 | } 122 | table { 123 | border-collapse: collapse; 124 | border-spacing: 0; 125 | } 126 | -------------------------------------------------------------------------------- /src/main/webapp/apidocs/css/screen.css: -------------------------------------------------------------------------------- 1 | /* Original style from softwaremaniacs.org (c) Ivan Sagalaev */ 2 | .swagger-section pre code { 3 | display: block; 4 | padding: 0.5em; 5 | background: #F0F0F0; 6 | } 7 | .swagger-section pre code, 8 | .swagger-section pre .subst, 9 | .swagger-section pre .tag .title, 10 | .swagger-section pre .lisp .title, 11 | .swagger-section pre .clojure .built_in, 12 | .swagger-section pre .nginx .title { 13 | color: black; 14 | } 15 | .swagger-section pre .string, 16 | .swagger-section pre .title, 17 | .swagger-section pre .constant, 18 | .swagger-section pre .parent, 19 | .swagger-section pre .tag .value, 20 | .swagger-section pre .rules .value, 21 | .swagger-section pre .rules .value .number, 22 | .swagger-section pre .preprocessor, 23 | .swagger-section pre .ruby .symbol, 24 | .swagger-section pre .ruby .symbol .string, 25 | .swagger-section pre .aggregate, 26 | .swagger-section pre .template_tag, 27 | .swagger-section pre .django .variable, 28 | .swagger-section pre .smalltalk .class, 29 | .swagger-section pre .addition, 30 | .swagger-section pre .flow, 31 | .swagger-section pre .stream, 32 | .swagger-section pre .bash .variable, 33 | .swagger-section pre .apache .tag, 34 | .swagger-section pre .apache .cbracket, 35 | .swagger-section pre .tex .command, 36 | .swagger-section pre .tex .special, 37 | .swagger-section pre .erlang_repl .function_or_atom, 38 | .swagger-section pre .markdown .header { 39 | color: #800; 40 | } 41 | .swagger-section pre .comment, 42 | .swagger-section pre .annotation, 43 | .swagger-section pre .template_comment, 44 | .swagger-section pre .diff .header, 45 | .swagger-section pre .chunk, 46 | .swagger-section pre .markdown .blockquote { 47 | color: #888; 48 | } 49 | .swagger-section pre .number, 50 | .swagger-section pre .date, 51 | .swagger-section pre .regexp, 52 | .swagger-section pre .literal, 53 | .swagger-section pre .smalltalk .symbol, 54 | .swagger-section pre .smalltalk .char, 55 | .swagger-section pre .go .constant, 56 | .swagger-section pre .change, 57 | .swagger-section pre .markdown .bullet, 58 | .swagger-section pre .markdown .link_url { 59 | color: #080; 60 | } 61 | .swagger-section pre .label, 62 | .swagger-section pre .javadoc, 63 | .swagger-section pre .ruby .string, 64 | .swagger-section pre .decorator, 65 | .swagger-section pre .filter .argument, 66 | .swagger-section pre .localvars, 67 | .swagger-section pre .array, 68 | .swagger-section pre .attr_selector, 69 | .swagger-section pre .important, 70 | .swagger-section pre .pseudo, 71 | .swagger-section pre .pi, 72 | .swagger-section pre .doctype, 73 | .swagger-section pre .deletion, 74 | .swagger-section pre .envvar, 75 | .swagger-section pre .shebang, 76 | .swagger-section pre .apache .sqbracket, 77 | .swagger-section pre .nginx .built_in, 78 | .swagger-section pre .tex .formula, 79 | .swagger-section pre .erlang_repl .reserved, 80 | .swagger-section pre .prompt, 81 | .swagger-section pre .markdown .link_label, 82 | .swagger-section pre .vhdl .attribute, 83 | .swagger-section pre .clojure .attribute, 84 | .swagger-section pre .coffeescript .property { 85 | color: #8888ff; 86 | } 87 | .swagger-section pre .keyword, 88 | .swagger-section pre .id, 89 | .swagger-section pre .phpdoc, 90 | .swagger-section pre .title, 91 | .swagger-section pre .built_in, 92 | .swagger-section pre .aggregate, 93 | .swagger-section pre .css .tag, 94 | .swagger-section pre .javadoctag, 95 | .swagger-section pre .phpdoc, 96 | .swagger-section pre .yardoctag, 97 | .swagger-section pre .smalltalk .class, 98 | .swagger-section pre .winutils, 99 | .swagger-section pre .bash .variable, 100 | .swagger-section pre .apache .tag, 101 | .swagger-section pre .go .typename, 102 | .swagger-section pre .tex .command, 103 | .swagger-section pre .markdown .strong, 104 | .swagger-section pre .request, 105 | .swagger-section pre .status { 106 | font-weight: bold; 107 | } 108 | .swagger-section pre .markdown .emphasis { 109 | font-style: italic; 110 | } 111 | .swagger-section pre .nginx .built_in { 112 | font-weight: normal; 113 | } 114 | .swagger-section pre .coffeescript .javascript, 115 | .swagger-section pre .javascript .xml, 116 | .swagger-section pre .tex .formula, 117 | .swagger-section pre .xml .javascript, 118 | .swagger-section pre .xml .vbscript, 119 | .swagger-section pre .xml .css, 120 | .swagger-section pre .xml .cdata { 121 | opacity: 0.5; 122 | } 123 | .swagger-section .swagger-ui-wrap { 124 | line-height: 1; 125 | font-family: "Droid Sans", sans-serif; 126 | max-width: 960px; 127 | margin-left: auto; 128 | margin-right: auto; 129 | } 130 | .swagger-section .swagger-ui-wrap b, 131 | .swagger-section .swagger-ui-wrap strong { 132 | font-family: "Droid Sans", sans-serif; 133 | font-weight: bold; 134 | } 135 | .swagger-section .swagger-ui-wrap q, 136 | .swagger-section .swagger-ui-wrap blockquote { 137 | quotes: none; 138 | } 139 | .swagger-section .swagger-ui-wrap p { 140 | line-height: 1.4em; 141 | padding: 0 0 10px; 142 | color: #333333; 143 | } 144 | .swagger-section .swagger-ui-wrap q:before, 145 | .swagger-section .swagger-ui-wrap q:after, 146 | .swagger-section .swagger-ui-wrap blockquote:before, 147 | .swagger-section .swagger-ui-wrap blockquote:after { 148 | content: none; 149 | } 150 | .swagger-section .swagger-ui-wrap .heading_with_menu h1, 151 | .swagger-section .swagger-ui-wrap .heading_with_menu h2, 152 | .swagger-section .swagger-ui-wrap .heading_with_menu h3, 153 | .swagger-section .swagger-ui-wrap .heading_with_menu h4, 154 | .swagger-section .swagger-ui-wrap .heading_with_menu h5, 155 | .swagger-section .swagger-ui-wrap .heading_with_menu h6 { 156 | display: block; 157 | clear: none; 158 | float: left; 159 | -moz-box-sizing: border-box; 160 | -webkit-box-sizing: border-box; 161 | -ms-box-sizing: border-box; 162 | box-sizing: border-box; 163 | width: 60%; 164 | } 165 | .swagger-section .swagger-ui-wrap table { 166 | border-collapse: collapse; 167 | border-spacing: 0; 168 | } 169 | .swagger-section .swagger-ui-wrap table thead tr th { 170 | padding: 5px; 171 | font-size: 0.9em; 172 | color: #666666; 173 | border-bottom: 1px solid #999999; 174 | } 175 | .swagger-section .swagger-ui-wrap table tbody tr:last-child td { 176 | border-bottom: none; 177 | } 178 | .swagger-section .swagger-ui-wrap table tbody tr.offset { 179 | background-color: #f0f0f0; 180 | } 181 | .swagger-section .swagger-ui-wrap table tbody tr td { 182 | padding: 6px; 183 | font-size: 0.9em; 184 | border-bottom: 1px solid #cccccc; 185 | vertical-align: top; 186 | line-height: 1.3em; 187 | } 188 | .swagger-section .swagger-ui-wrap ol { 189 | margin: 0px 0 10px; 190 | padding: 0 0 0 18px; 191 | list-style-type: decimal; 192 | } 193 | .swagger-section .swagger-ui-wrap ol li { 194 | padding: 5px 0px; 195 | font-size: 0.9em; 196 | color: #333333; 197 | } 198 | .swagger-section .swagger-ui-wrap ol, 199 | .swagger-section .swagger-ui-wrap ul { 200 | list-style: none; 201 | } 202 | .swagger-section .swagger-ui-wrap h1 a, 203 | .swagger-section .swagger-ui-wrap h2 a, 204 | .swagger-section .swagger-ui-wrap h3 a, 205 | .swagger-section .swagger-ui-wrap h4 a, 206 | .swagger-section .swagger-ui-wrap h5 a, 207 | .swagger-section .swagger-ui-wrap h6 a { 208 | text-decoration: none; 209 | } 210 | .swagger-section .swagger-ui-wrap h1 a:hover, 211 | .swagger-section .swagger-ui-wrap h2 a:hover, 212 | .swagger-section .swagger-ui-wrap h3 a:hover, 213 | .swagger-section .swagger-ui-wrap h4 a:hover, 214 | .swagger-section .swagger-ui-wrap h5 a:hover, 215 | .swagger-section .swagger-ui-wrap h6 a:hover { 216 | text-decoration: underline; 217 | } 218 | .swagger-section .swagger-ui-wrap h1 span.divider, 219 | .swagger-section .swagger-ui-wrap h2 span.divider, 220 | .swagger-section .swagger-ui-wrap h3 span.divider, 221 | .swagger-section .swagger-ui-wrap h4 span.divider, 222 | .swagger-section .swagger-ui-wrap h5 span.divider, 223 | .swagger-section .swagger-ui-wrap h6 span.divider { 224 | color: #aaaaaa; 225 | } 226 | .swagger-section .swagger-ui-wrap a { 227 | color: #547f00; 228 | } 229 | .swagger-section .swagger-ui-wrap a img { 230 | border: none; 231 | } 232 | .swagger-section .swagger-ui-wrap article, 233 | .swagger-section .swagger-ui-wrap aside, 234 | .swagger-section .swagger-ui-wrap details, 235 | .swagger-section .swagger-ui-wrap figcaption, 236 | .swagger-section .swagger-ui-wrap figure, 237 | .swagger-section .swagger-ui-wrap footer, 238 | .swagger-section .swagger-ui-wrap header, 239 | .swagger-section .swagger-ui-wrap hgroup, 240 | .swagger-section .swagger-ui-wrap menu, 241 | .swagger-section .swagger-ui-wrap nav, 242 | .swagger-section .swagger-ui-wrap section, 243 | .swagger-section .swagger-ui-wrap summary { 244 | display: block; 245 | } 246 | .swagger-section .swagger-ui-wrap pre { 247 | font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; 248 | background-color: #fcf6db; 249 | border: 1px solid #e5e0c6; 250 | padding: 10px; 251 | } 252 | .swagger-section .swagger-ui-wrap pre code { 253 | line-height: 1.6em; 254 | background: none; 255 | } 256 | .swagger-section .swagger-ui-wrap .content > .content-type > div > label { 257 | clear: both; 258 | display: block; 259 | color: #0F6AB4; 260 | font-size: 1.1em; 261 | margin: 0; 262 | padding: 15px 0 5px; 263 | } 264 | .swagger-section .swagger-ui-wrap .content pre { 265 | font-size: 12px; 266 | margin-top: 5px; 267 | padding: 5px; 268 | } 269 | .swagger-section .swagger-ui-wrap .icon-btn { 270 | cursor: pointer; 271 | } 272 | .swagger-section .swagger-ui-wrap .info_title { 273 | padding-bottom: 10px; 274 | font-weight: bold; 275 | font-size: 25px; 276 | } 277 | .swagger-section .swagger-ui-wrap p.big, 278 | .swagger-section .swagger-ui-wrap div.big p { 279 | font-size: 1em; 280 | margin-bottom: 10px; 281 | } 282 | .swagger-section .swagger-ui-wrap form.fullwidth ol li.string input, 283 | .swagger-section .swagger-ui-wrap form.fullwidth ol li.url input, 284 | .swagger-section .swagger-ui-wrap form.fullwidth ol li.text textarea, 285 | .swagger-section .swagger-ui-wrap form.fullwidth ol li.numeric input { 286 | width: 500px !important; 287 | } 288 | .swagger-section .swagger-ui-wrap .info_license { 289 | padding-bottom: 5px; 290 | } 291 | .swagger-section .swagger-ui-wrap .info_tos { 292 | padding-bottom: 5px; 293 | } 294 | .swagger-section .swagger-ui-wrap .message-fail { 295 | color: #cc0000; 296 | } 297 | .swagger-section .swagger-ui-wrap .info_contact { 298 | padding-bottom: 5px; 299 | } 300 | .swagger-section .swagger-ui-wrap .info_description { 301 | padding-bottom: 10px; 302 | font-size: 15px; 303 | } 304 | .swagger-section .swagger-ui-wrap .markdown ol li, 305 | .swagger-section .swagger-ui-wrap .markdown ul li { 306 | padding: 3px 0px; 307 | line-height: 1.4em; 308 | color: #333333; 309 | } 310 | .swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.string input, 311 | .swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.url input, 312 | .swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.numeric input { 313 | display: block; 314 | padding: 4px; 315 | width: auto; 316 | clear: both; 317 | } 318 | .swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.string input.title, 319 | .swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.url input.title, 320 | .swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.numeric input.title { 321 | font-size: 1.3em; 322 | } 323 | .swagger-section .swagger-ui-wrap table.fullwidth { 324 | width: 100%; 325 | } 326 | .swagger-section .swagger-ui-wrap .model-signature { 327 | font-family: "Droid Sans", sans-serif; 328 | font-size: 1em; 329 | line-height: 1.5em; 330 | } 331 | .swagger-section .swagger-ui-wrap .model-signature .signature-nav a { 332 | text-decoration: none; 333 | color: #AAA; 334 | } 335 | .swagger-section .swagger-ui-wrap .model-signature .signature-nav a:hover { 336 | text-decoration: underline; 337 | color: black; 338 | } 339 | .swagger-section .swagger-ui-wrap .model-signature .signature-nav .selected { 340 | color: black; 341 | text-decoration: none; 342 | } 343 | .swagger-section .swagger-ui-wrap .model-signature .propType { 344 | color: #5555aa; 345 | } 346 | .swagger-section .swagger-ui-wrap .model-signature pre:hover { 347 | background-color: #ffffdd; 348 | } 349 | .swagger-section .swagger-ui-wrap .model-signature pre { 350 | font-size: .85em; 351 | line-height: 1.2em; 352 | overflow: auto; 353 | max-height: 200px; 354 | cursor: pointer; 355 | } 356 | .swagger-section .swagger-ui-wrap .model-signature ul.signature-nav { 357 | display: block; 358 | margin: 0; 359 | padding: 0; 360 | } 361 | .swagger-section .swagger-ui-wrap .model-signature ul.signature-nav li:last-child { 362 | padding-right: 0; 363 | border-right: none; 364 | } 365 | .swagger-section .swagger-ui-wrap .model-signature ul.signature-nav li { 366 | float: left; 367 | margin: 0 5px 5px 0; 368 | padding: 2px 5px 2px 0; 369 | border-right: 1px solid #ddd; 370 | } 371 | .swagger-section .swagger-ui-wrap .model-signature .propOpt { 372 | color: #555; 373 | } 374 | .swagger-section .swagger-ui-wrap .model-signature .snippet small { 375 | font-size: 0.75em; 376 | } 377 | .swagger-section .swagger-ui-wrap .model-signature .propOptKey { 378 | font-style: italic; 379 | } 380 | .swagger-section .swagger-ui-wrap .model-signature .description .strong { 381 | font-weight: bold; 382 | color: #000; 383 | font-size: .9em; 384 | } 385 | .swagger-section .swagger-ui-wrap .model-signature .description div { 386 | font-size: 0.9em; 387 | line-height: 1.5em; 388 | margin-left: 1em; 389 | } 390 | .swagger-section .swagger-ui-wrap .model-signature .description .stronger { 391 | font-weight: bold; 392 | color: #000; 393 | } 394 | .swagger-section .swagger-ui-wrap .model-signature .propName { 395 | font-weight: bold; 396 | } 397 | .swagger-section .swagger-ui-wrap .model-signature .signature-container { 398 | clear: both; 399 | } 400 | .swagger-section .swagger-ui-wrap .body-textarea { 401 | width: 300px; 402 | height: 100px; 403 | border: 1px solid #aaa; 404 | } 405 | .swagger-section .swagger-ui-wrap .markdown p code, 406 | .swagger-section .swagger-ui-wrap .markdown li code { 407 | font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; 408 | background-color: #f0f0f0; 409 | color: black; 410 | padding: 1px 3px; 411 | } 412 | .swagger-section .swagger-ui-wrap .required { 413 | font-weight: bold; 414 | } 415 | .swagger-section .swagger-ui-wrap input.parameter { 416 | width: 300px; 417 | border: 1px solid #aaa; 418 | } 419 | .swagger-section .swagger-ui-wrap h1 { 420 | color: black; 421 | font-size: 1.5em; 422 | line-height: 1.3em; 423 | padding: 10px 0 10px 0; 424 | font-family: "Droid Sans", sans-serif; 425 | font-weight: bold; 426 | } 427 | .swagger-section .swagger-ui-wrap .heading_with_menu { 428 | float: none; 429 | clear: both; 430 | overflow: hidden; 431 | display: block; 432 | } 433 | .swagger-section .swagger-ui-wrap .heading_with_menu ul { 434 | display: block; 435 | clear: none; 436 | float: right; 437 | -moz-box-sizing: border-box; 438 | -webkit-box-sizing: border-box; 439 | -ms-box-sizing: border-box; 440 | box-sizing: border-box; 441 | margin-top: 10px; 442 | } 443 | .swagger-section .swagger-ui-wrap h2 { 444 | color: black; 445 | font-size: 1.3em; 446 | padding: 10px 0 10px 0; 447 | } 448 | .swagger-section .swagger-ui-wrap h2 a { 449 | color: black; 450 | } 451 | .swagger-section .swagger-ui-wrap h2 span.sub { 452 | font-size: 0.7em; 453 | color: #999999; 454 | font-style: italic; 455 | } 456 | .swagger-section .swagger-ui-wrap h2 span.sub a { 457 | color: #777777; 458 | } 459 | .swagger-section .swagger-ui-wrap span.weak { 460 | color: #666666; 461 | } 462 | .swagger-section .swagger-ui-wrap .message-success { 463 | color: #89BF04; 464 | } 465 | .swagger-section .swagger-ui-wrap caption, 466 | .swagger-section .swagger-ui-wrap th, 467 | .swagger-section .swagger-ui-wrap td { 468 | text-align: left; 469 | font-weight: normal; 470 | vertical-align: middle; 471 | } 472 | .swagger-section .swagger-ui-wrap .code { 473 | font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; 474 | } 475 | .swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.text textarea { 476 | font-family: "Droid Sans", sans-serif; 477 | height: 250px; 478 | padding: 4px; 479 | display: block; 480 | clear: both; 481 | } 482 | .swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.select select { 483 | display: block; 484 | clear: both; 485 | } 486 | .swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.boolean { 487 | float: none; 488 | clear: both; 489 | overflow: hidden; 490 | display: block; 491 | } 492 | .swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.boolean label { 493 | display: block; 494 | float: left; 495 | clear: none; 496 | margin: 0; 497 | padding: 0; 498 | } 499 | .swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.boolean input { 500 | display: block; 501 | float: left; 502 | clear: none; 503 | margin: 0 5px 0 0; 504 | } 505 | .swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.required label { 506 | color: black; 507 | } 508 | .swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li label { 509 | display: block; 510 | clear: both; 511 | width: auto; 512 | padding: 0 0 3px; 513 | color: #666666; 514 | } 515 | .swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li label abbr { 516 | padding-left: 3px; 517 | color: #888888; 518 | } 519 | .swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li p.inline-hints { 520 | margin-left: 0; 521 | font-style: italic; 522 | font-size: 0.9em; 523 | margin: 0; 524 | } 525 | .swagger-section .swagger-ui-wrap form.formtastic fieldset.buttons { 526 | margin: 0; 527 | padding: 0; 528 | } 529 | .swagger-section .swagger-ui-wrap span.blank, 530 | .swagger-section .swagger-ui-wrap span.empty { 531 | color: #888888; 532 | font-style: italic; 533 | } 534 | .swagger-section .swagger-ui-wrap .markdown h3 { 535 | color: #547f00; 536 | } 537 | .swagger-section .swagger-ui-wrap .markdown h4 { 538 | color: #666666; 539 | } 540 | .swagger-section .swagger-ui-wrap .markdown pre { 541 | font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; 542 | background-color: #fcf6db; 543 | border: 1px solid #e5e0c6; 544 | padding: 10px; 545 | margin: 0 0 10px 0; 546 | } 547 | .swagger-section .swagger-ui-wrap .markdown pre code { 548 | line-height: 1.6em; 549 | } 550 | .swagger-section .swagger-ui-wrap div.gist { 551 | margin: 20px 0 25px 0 !important; 552 | } 553 | .swagger-section .swagger-ui-wrap ul#resources { 554 | font-family: "Droid Sans", sans-serif; 555 | font-size: 0.9em; 556 | } 557 | .swagger-section .swagger-ui-wrap ul#resources li.resource { 558 | border-bottom: 1px solid #dddddd; 559 | } 560 | .swagger-section .swagger-ui-wrap ul#resources li.resource:hover div.heading h2 a, 561 | .swagger-section .swagger-ui-wrap ul#resources li.resource.active div.heading h2 a { 562 | color: black; 563 | } 564 | .swagger-section .swagger-ui-wrap ul#resources li.resource:hover div.heading ul.options li a, 565 | .swagger-section .swagger-ui-wrap ul#resources li.resource.active div.heading ul.options li a { 566 | color: #555555; 567 | } 568 | .swagger-section .swagger-ui-wrap ul#resources li.resource:last-child { 569 | border-bottom: none; 570 | } 571 | .swagger-section .swagger-ui-wrap ul#resources li.resource div.heading { 572 | border: 1px solid transparent; 573 | float: none; 574 | clear: both; 575 | overflow: hidden; 576 | display: block; 577 | } 578 | .swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options { 579 | overflow: hidden; 580 | padding: 0; 581 | display: block; 582 | clear: none; 583 | float: right; 584 | margin: 14px 10px 0 0; 585 | } 586 | .swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li { 587 | float: left; 588 | clear: none; 589 | margin: 0; 590 | padding: 2px 10px; 591 | border-right: 1px solid #dddddd; 592 | color: #666666; 593 | font-size: 0.9em; 594 | } 595 | .swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a { 596 | color: #aaaaaa; 597 | text-decoration: none; 598 | } 599 | .swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a:hover { 600 | text-decoration: underline; 601 | color: black; 602 | } 603 | .swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a:hover, 604 | .swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a:active, 605 | .swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a.active { 606 | text-decoration: underline; 607 | } 608 | .swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li:first-child, 609 | .swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li.first { 610 | padding-left: 0; 611 | } 612 | .swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li:last-child, 613 | .swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li.last { 614 | padding-right: 0; 615 | border-right: none; 616 | } 617 | .swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options:first-child, 618 | .swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options.first { 619 | padding-left: 0; 620 | } 621 | .swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 { 622 | color: #999999; 623 | padding-left: 0; 624 | display: block; 625 | clear: none; 626 | float: left; 627 | font-family: "Droid Sans", sans-serif; 628 | font-weight: bold; 629 | } 630 | .swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 a { 631 | color: #999999; 632 | } 633 | .swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 a:hover { 634 | color: black; 635 | } 636 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation { 637 | float: none; 638 | clear: both; 639 | overflow: hidden; 640 | display: block; 641 | margin: 0 0 10px; 642 | padding: 0; 643 | } 644 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading { 645 | float: none; 646 | clear: both; 647 | overflow: hidden; 648 | display: block; 649 | margin: 0; 650 | padding: 0; 651 | } 652 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 { 653 | display: block; 654 | clear: none; 655 | float: left; 656 | width: auto; 657 | margin: 0; 658 | padding: 0; 659 | line-height: 1.1em; 660 | color: black; 661 | } 662 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path { 663 | padding-left: 10px; 664 | } 665 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a { 666 | color: black; 667 | text-decoration: none; 668 | } 669 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a:hover { 670 | text-decoration: underline; 671 | } 672 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.http_method a { 673 | text-transform: uppercase; 674 | text-decoration: none; 675 | color: white; 676 | display: inline-block; 677 | width: 50px; 678 | font-size: 0.7em; 679 | text-align: center; 680 | padding: 7px 0 4px; 681 | -moz-border-radius: 2px; 682 | -webkit-border-radius: 2px; 683 | -o-border-radius: 2px; 684 | -ms-border-radius: 2px; 685 | -khtml-border-radius: 2px; 686 | border-radius: 2px; 687 | } 688 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span { 689 | margin: 0; 690 | padding: 0; 691 | } 692 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options { 693 | overflow: hidden; 694 | padding: 0; 695 | display: block; 696 | clear: none; 697 | float: right; 698 | margin: 6px 10px 0 0; 699 | } 700 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li { 701 | float: left; 702 | clear: none; 703 | margin: 0; 704 | padding: 2px 10px; 705 | font-size: 0.9em; 706 | } 707 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li a { 708 | text-decoration: none; 709 | } 710 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li.access { 711 | color: black; 712 | } 713 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content { 714 | border-top: none; 715 | padding: 10px; 716 | -moz-border-radius-bottomleft: 6px; 717 | -webkit-border-bottom-left-radius: 6px; 718 | -o-border-bottom-left-radius: 6px; 719 | -ms-border-bottom-left-radius: 6px; 720 | -khtml-border-bottom-left-radius: 6px; 721 | border-bottom-left-radius: 6px; 722 | -moz-border-radius-bottomright: 6px; 723 | -webkit-border-bottom-right-radius: 6px; 724 | -o-border-bottom-right-radius: 6px; 725 | -ms-border-bottom-right-radius: 6px; 726 | -khtml-border-bottom-right-radius: 6px; 727 | border-bottom-right-radius: 6px; 728 | margin: 0 0 20px; 729 | } 730 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content h4 { 731 | font-size: 1.1em; 732 | margin: 0; 733 | padding: 15px 0 5px; 734 | } 735 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header { 736 | float: none; 737 | clear: both; 738 | overflow: hidden; 739 | display: block; 740 | } 741 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header a { 742 | padding: 4px 0 0 10px; 743 | display: inline-block; 744 | font-size: 0.9em; 745 | } 746 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header input.submit { 747 | display: block; 748 | clear: none; 749 | float: left; 750 | padding: 6px 8px; 751 | } 752 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header span.response_throbber { 753 | background-image: url('../images/throbber.gif'); 754 | width: 128px; 755 | height: 16px; 756 | display: block; 757 | clear: none; 758 | float: right; 759 | } 760 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content form input[type='text'].error { 761 | outline: 2px solid black; 762 | outline-color: #cc0000; 763 | } 764 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.response div.block pre { 765 | font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; 766 | padding: 10px; 767 | font-size: 0.9em; 768 | max-height: 400px; 769 | overflow-y: auto; 770 | } 771 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading { 772 | background-color: #f9f2e9; 773 | border: 1px solid #f0e0ca; 774 | } 775 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading h3 span.http_method a { 776 | background-color: #c5862b; 777 | } 778 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li { 779 | border-right: 1px solid #dddddd; 780 | border-right-color: #f0e0ca; 781 | color: #c5862b; 782 | } 783 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li a { 784 | color: #c5862b; 785 | } 786 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content { 787 | background-color: #faf5ee; 788 | border: 1px solid #f0e0ca; 789 | } 790 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content h4 { 791 | color: #c5862b; 792 | } 793 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content div.sandbox_header a { 794 | color: #dcb67f; 795 | } 796 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading { 797 | background-color: #fcffcd; 798 | border: 1px solid black; 799 | border-color: #ffd20f; 800 | } 801 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading h3 span.http_method a { 802 | text-transform: uppercase; 803 | background-color: #ffd20f; 804 | } 805 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li { 806 | border-right: 1px solid #dddddd; 807 | border-right-color: #ffd20f; 808 | color: #ffd20f; 809 | } 810 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li a { 811 | color: #ffd20f; 812 | } 813 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content { 814 | background-color: #fcffcd; 815 | border: 1px solid black; 816 | border-color: #ffd20f; 817 | } 818 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content h4 { 819 | color: #ffd20f; 820 | } 821 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content div.sandbox_header a { 822 | color: #6fc992; 823 | } 824 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading { 825 | background-color: #f5e8e8; 826 | border: 1px solid #e8c6c7; 827 | } 828 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading h3 span.http_method a { 829 | text-transform: uppercase; 830 | background-color: #a41e22; 831 | } 832 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li { 833 | border-right: 1px solid #dddddd; 834 | border-right-color: #e8c6c7; 835 | color: #a41e22; 836 | } 837 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li a { 838 | color: #a41e22; 839 | } 840 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content { 841 | background-color: #f7eded; 842 | border: 1px solid #e8c6c7; 843 | } 844 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content h4 { 845 | color: #a41e22; 846 | } 847 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content div.sandbox_header a { 848 | color: #c8787a; 849 | } 850 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading { 851 | background-color: #e7f6ec; 852 | border: 1px solid #c3e8d1; 853 | } 854 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading h3 span.http_method a { 855 | background-color: #10a54a; 856 | } 857 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li { 858 | border-right: 1px solid #dddddd; 859 | border-right-color: #c3e8d1; 860 | color: #10a54a; 861 | } 862 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li a { 863 | color: #10a54a; 864 | } 865 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content { 866 | background-color: #ebf7f0; 867 | border: 1px solid #c3e8d1; 868 | } 869 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content h4 { 870 | color: #10a54a; 871 | } 872 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content div.sandbox_header a { 873 | color: #6fc992; 874 | } 875 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading { 876 | background-color: #FCE9E3; 877 | border: 1px solid #F5D5C3; 878 | } 879 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading h3 span.http_method a { 880 | background-color: #D38042; 881 | } 882 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li { 883 | border-right: 1px solid #dddddd; 884 | border-right-color: #f0cecb; 885 | color: #D38042; 886 | } 887 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li a { 888 | color: #D38042; 889 | } 890 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content { 891 | background-color: #faf0ef; 892 | border: 1px solid #f0cecb; 893 | } 894 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content h4 { 895 | color: #D38042; 896 | } 897 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content div.sandbox_header a { 898 | color: #dcb67f; 899 | } 900 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading { 901 | background-color: #e7f0f7; 902 | border: 1px solid #c3d9ec; 903 | } 904 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading h3 span.http_method a { 905 | background-color: #0f6ab4; 906 | } 907 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li { 908 | border-right: 1px solid #dddddd; 909 | border-right-color: #c3d9ec; 910 | color: #0f6ab4; 911 | } 912 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li a { 913 | color: #0f6ab4; 914 | } 915 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content { 916 | background-color: #ebf3f9; 917 | border: 1px solid #c3d9ec; 918 | } 919 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content h4 { 920 | color: #0f6ab4; 921 | } 922 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content div.sandbox_header a { 923 | color: #6fa5d2; 924 | } 925 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading { 926 | background-color: #e7f0f7; 927 | border: 1px solid #c3d9ec; 928 | } 929 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading h3 span.http_method a { 930 | background-color: #0f6ab4; 931 | } 932 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading ul.options li { 933 | border-right: 1px solid #dddddd; 934 | border-right-color: #c3d9ec; 935 | color: #0f6ab4; 936 | } 937 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading ul.options li a { 938 | color: #0f6ab4; 939 | } 940 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.content { 941 | background-color: #ebf3f9; 942 | border: 1px solid #c3d9ec; 943 | } 944 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.content h4 { 945 | color: #0f6ab4; 946 | } 947 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.content div.sandbox_header a { 948 | color: #6fa5d2; 949 | } 950 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content, 951 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content, 952 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content, 953 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content, 954 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content, 955 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content { 956 | border-top: none; 957 | } 958 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li:last-child, 959 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li:last-child, 960 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li:last-child, 961 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li:last-child, 962 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li:last-child, 963 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li:last-child, 964 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li.last, 965 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li.last, 966 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li.last, 967 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li.last, 968 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li.last, 969 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li.last { 970 | padding-right: 0; 971 | border-right: none; 972 | } 973 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li a:hover, 974 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li a:active, 975 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li a.active { 976 | text-decoration: underline; 977 | } 978 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li:first-child, 979 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li.first { 980 | padding-left: 0; 981 | } 982 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations:first-child, 983 | .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations.first { 984 | padding-left: 0; 985 | } 986 | .swagger-section .swagger-ui-wrap p#colophon { 987 | margin: 0 15px 40px 15px; 988 | padding: 10px 0; 989 | font-size: 0.8em; 990 | border-top: 1px solid #dddddd; 991 | font-family: "Droid Sans", sans-serif; 992 | color: #999999; 993 | font-style: italic; 994 | } 995 | .swagger-section .swagger-ui-wrap p#colophon a { 996 | text-decoration: none; 997 | color: #547f00; 998 | } 999 | .swagger-section .swagger-ui-wrap h3 { 1000 | color: black; 1001 | font-size: 1.1em; 1002 | padding: 10px 0 10px 0; 1003 | } 1004 | .swagger-section .swagger-ui-wrap .markdown ol, 1005 | .swagger-section .swagger-ui-wrap .markdown ul { 1006 | font-family: "Droid Sans", sans-serif; 1007 | margin: 5px 0 10px; 1008 | padding: 0 0 0 18px; 1009 | list-style-type: disc; 1010 | } 1011 | .swagger-section .swagger-ui-wrap form.form_box { 1012 | background-color: #ebf3f9; 1013 | border: 1px solid #c3d9ec; 1014 | padding: 10px; 1015 | } 1016 | .swagger-section .swagger-ui-wrap form.form_box label { 1017 | color: #0f6ab4 !important; 1018 | } 1019 | .swagger-section .swagger-ui-wrap form.form_box input[type=submit] { 1020 | display: block; 1021 | padding: 10px; 1022 | } 1023 | .swagger-section .swagger-ui-wrap form.form_box p.weak { 1024 | font-size: 0.8em; 1025 | } 1026 | .swagger-section .swagger-ui-wrap form.form_box p { 1027 | font-size: 0.9em; 1028 | padding: 0 0 15px; 1029 | color: #7e7b6d; 1030 | } 1031 | .swagger-section .swagger-ui-wrap form.form_box p a { 1032 | color: #646257; 1033 | } 1034 | .swagger-section .swagger-ui-wrap form.form_box p strong { 1035 | color: black; 1036 | } 1037 | .swagger-section .title { 1038 | font-style: bold; 1039 | } 1040 | .swagger-section .secondary_form { 1041 | display: none; 1042 | } 1043 | .swagger-section .main_image { 1044 | display: block; 1045 | margin-left: auto; 1046 | margin-right: auto; 1047 | } 1048 | .swagger-section .oauth_body { 1049 | margin-left: 100px; 1050 | margin-right: 100px; 1051 | } 1052 | .swagger-section .oauth_submit { 1053 | text-align: center; 1054 | } 1055 | .swagger-section .api-popup-dialog { 1056 | z-index: 10000; 1057 | position: absolute; 1058 | width: 500px; 1059 | background: #FFF; 1060 | padding: 20px; 1061 | border: 1px solid #ccc; 1062 | border-radius: 5px; 1063 | display: none; 1064 | font-size: 13px; 1065 | color: #777; 1066 | } 1067 | .swagger-section .api-popup-dialog .api-popup-title { 1068 | font-size: 24px; 1069 | padding: 10px 0; 1070 | } 1071 | .swagger-section .api-popup-dialog .api-popup-title { 1072 | font-size: 24px; 1073 | padding: 10px 0; 1074 | } 1075 | .swagger-section .api-popup-dialog p.error-msg { 1076 | padding-left: 5px; 1077 | padding-bottom: 5px; 1078 | } 1079 | .swagger-section .api-popup-dialog button.api-popup-authbtn { 1080 | height: 30px; 1081 | } 1082 | .swagger-section .api-popup-dialog button.api-popup-cancel { 1083 | height: 30px; 1084 | } 1085 | .swagger-section .api-popup-scopes { 1086 | padding: 10px 20px; 1087 | } 1088 | .swagger-section .api-popup-scopes li { 1089 | padding: 5px 0; 1090 | line-height: 20px; 1091 | } 1092 | .swagger-section .api-popup-scopes .api-scope-desc { 1093 | padding-left: 20px; 1094 | font-style: italic; 1095 | } 1096 | .swagger-section .api-popup-scopes li input { 1097 | position: relative; 1098 | top: 2px; 1099 | } 1100 | .swagger-section .api-popup-actions { 1101 | padding-top: 10px; 1102 | } 1103 | .swagger-section .access { 1104 | float: right; 1105 | } 1106 | .swagger-section .auth { 1107 | float: right; 1108 | } 1109 | .swagger-section #api_information_panel { 1110 | position: absolute; 1111 | background: #FFF; 1112 | border: 1px solid #ccc; 1113 | border-radius: 5px; 1114 | display: none; 1115 | font-size: 13px; 1116 | max-width: 300px; 1117 | line-height: 30px; 1118 | color: black; 1119 | padding: 5px; 1120 | } 1121 | .swagger-section #api_information_panel p .api-msg-enabled { 1122 | color: green; 1123 | } 1124 | .swagger-section #api_information_panel p .api-msg-disabled { 1125 | color: red; 1126 | } 1127 | .swagger-section .api-ic { 1128 | height: 18px; 1129 | vertical-align: middle; 1130 | display: inline-block; 1131 | background: url(../images/explorer_icons.png) no-repeat; 1132 | } 1133 | .swagger-section .ic-info { 1134 | background-position: 0 0; 1135 | width: 18px; 1136 | margin-top: -7px; 1137 | margin-left: 4px; 1138 | } 1139 | .swagger-section .ic-warning { 1140 | background-position: -60px 0; 1141 | width: 18px; 1142 | margin-top: -7px; 1143 | margin-left: 4px; 1144 | } 1145 | .swagger-section .ic-error { 1146 | background-position: -30px 0; 1147 | width: 18px; 1148 | margin-top: -7px; 1149 | margin-left: 4px; 1150 | } 1151 | .swagger-section .ic-off { 1152 | background-position: -90px 0; 1153 | width: 58px; 1154 | margin-top: -4px; 1155 | cursor: pointer; 1156 | } 1157 | .swagger-section .ic-on { 1158 | background-position: -160px 0; 1159 | width: 58px; 1160 | margin-top: -4px; 1161 | cursor: pointer; 1162 | } 1163 | .swagger-section #header { 1164 | background-color: #89bf04; 1165 | padding: 14px; 1166 | } 1167 | .swagger-section #header a#logo { 1168 | font-size: 1.5em; 1169 | font-weight: bold; 1170 | text-decoration: none; 1171 | background: transparent url(../images/logo_small.png) no-repeat left center; 1172 | padding: 20px 0 20px 40px; 1173 | color: white; 1174 | } 1175 | .swagger-section #header form#api_selector { 1176 | display: block; 1177 | clear: none; 1178 | float: right; 1179 | } 1180 | .swagger-section #header form#api_selector .input { 1181 | display: block; 1182 | clear: none; 1183 | float: left; 1184 | margin: 0 10px 0 0; 1185 | } 1186 | .swagger-section #header form#api_selector .input input#input_apiKey { 1187 | width: 200px; 1188 | } 1189 | .swagger-section #header form#api_selector .input input#input_baseUrl { 1190 | width: 400px; 1191 | } 1192 | .swagger-section #header form#api_selector .input a#explore { 1193 | display: block; 1194 | text-decoration: none; 1195 | font-weight: bold; 1196 | padding: 6px 8px; 1197 | font-size: 0.9em; 1198 | color: white; 1199 | background-color: #547f00; 1200 | -moz-border-radius: 4px; 1201 | -webkit-border-radius: 4px; 1202 | -o-border-radius: 4px; 1203 | -ms-border-radius: 4px; 1204 | -khtml-border-radius: 4px; 1205 | border-radius: 4px; 1206 | } 1207 | .swagger-section #header form#api_selector .input a#explore:hover { 1208 | background-color: #547f00; 1209 | } 1210 | .swagger-section #header form#api_selector .input input { 1211 | font-size: 0.9em; 1212 | padding: 3px; 1213 | margin: 0; 1214 | } 1215 | .swagger-section #content_message { 1216 | margin: 10px 15px; 1217 | font-style: italic; 1218 | color: #999999; 1219 | } 1220 | .swagger-section #message-bar { 1221 | min-height: 30px; 1222 | text-align: center; 1223 | padding-top: 10px; 1224 | } 1225 | -------------------------------------------------------------------------------- /src/main/webapp/apidocs/images/explorer_icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakubStas/SpringWithSwagger/96b9e77ed711bfb1991d9f16d93eac388a5648e3/src/main/webapp/apidocs/images/explorer_icons.png -------------------------------------------------------------------------------- /src/main/webapp/apidocs/images/logo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakubStas/SpringWithSwagger/96b9e77ed711bfb1991d9f16d93eac388a5648e3/src/main/webapp/apidocs/images/logo_small.png -------------------------------------------------------------------------------- /src/main/webapp/apidocs/images/pet_store_api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakubStas/SpringWithSwagger/96b9e77ed711bfb1991d9f16d93eac388a5648e3/src/main/webapp/apidocs/images/pet_store_api.png -------------------------------------------------------------------------------- /src/main/webapp/apidocs/images/throbber.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakubStas/SpringWithSwagger/96b9e77ed711bfb1991d9f16d93eac388a5648e3/src/main/webapp/apidocs/images/throbber.gif -------------------------------------------------------------------------------- /src/main/webapp/apidocs/images/wordnik_api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakubStas/SpringWithSwagger/96b9e77ed711bfb1991d9f16d93eac388a5648e3/src/main/webapp/apidocs/images/wordnik_api.png -------------------------------------------------------------------------------- /src/main/webapp/apidocs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Swagger UI 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 65 | 66 | 67 | 68 | 84 | 85 |
 
86 |
87 | 88 | 89 | -------------------------------------------------------------------------------- /src/main/webapp/apidocs/lib/backbone-min.js: -------------------------------------------------------------------------------- 1 | // Backbone.js 0.9.2 2 | 3 | // (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc. 4 | // Backbone may be freely distributed under the MIT license. 5 | // For all details and documentation: 6 | // http://backbonejs.org 7 | (function(){var l=this,y=l.Backbone,z=Array.prototype.slice,A=Array.prototype.splice,g;g="undefined"!==typeof exports?exports:l.Backbone={};g.VERSION="0.9.2";var f=l._;!f&&"undefined"!==typeof require&&(f=require("underscore"));var i=l.jQuery||l.Zepto||l.ender;g.setDomLibrary=function(a){i=a};g.noConflict=function(){l.Backbone=y;return this};g.emulateHTTP=!1;g.emulateJSON=!1;var p=/\s+/,k=g.Events={on:function(a,b,c){var d,e,f,g,j;if(!b)return this;a=a.split(p);for(d=this._callbacks||(this._callbacks= 8 | {});e=a.shift();)f=(j=d[e])?j.tail:{},f.next=g={},f.context=c,f.callback=b,d[e]={tail:g,next:j?j.next:f};return this},off:function(a,b,c){var d,e,h,g,j,q;if(e=this._callbacks){if(!a&&!b&&!c)return delete this._callbacks,this;for(a=a?a.split(p):f.keys(e);d=a.shift();)if(h=e[d],delete e[d],h&&(b||c))for(g=h.tail;(h=h.next)!==g;)if(j=h.callback,q=h.context,b&&j!==b||c&&q!==c)this.on(d,j,q);return this}},trigger:function(a){var b,c,d,e,f,g;if(!(d=this._callbacks))return this;f=d.all;a=a.split(p);for(g= 9 | z.call(arguments,1);b=a.shift();){if(c=d[b])for(e=c.tail;(c=c.next)!==e;)c.callback.apply(c.context||this,g);if(c=f){e=c.tail;for(b=[b].concat(g);(c=c.next)!==e;)c.callback.apply(c.context||this,b)}}return this}};k.bind=k.on;k.unbind=k.off;var o=g.Model=function(a,b){var c;a||(a={});b&&b.parse&&(a=this.parse(a));if(c=n(this,"defaults"))a=f.extend({},c,a);b&&b.collection&&(this.collection=b.collection);this.attributes={};this._escapedAttributes={};this.cid=f.uniqueId("c");this.changed={};this._silent= 10 | {};this._pending={};this.set(a,{silent:!0});this.changed={};this._silent={};this._pending={};this._previousAttributes=f.clone(this.attributes);this.initialize.apply(this,arguments)};f.extend(o.prototype,k,{changed:null,_silent:null,_pending:null,idAttribute:"id",initialize:function(){},toJSON:function(){return f.clone(this.attributes)},get:function(a){return this.attributes[a]},escape:function(a){var b;if(b=this._escapedAttributes[a])return b;b=this.get(a);return this._escapedAttributes[a]=f.escape(null== 11 | b?"":""+b)},has:function(a){return null!=this.get(a)},set:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c||(c={});if(!d)return this;d instanceof o&&(d=d.attributes);if(c.unset)for(e in d)d[e]=void 0;if(!this._validate(d,c))return!1;this.idAttribute in d&&(this.id=d[this.idAttribute]);var b=c.changes={},h=this.attributes,g=this._escapedAttributes,j=this._previousAttributes||{};for(e in d){a=d[e];if(!f.isEqual(h[e],a)||c.unset&&f.has(h,e))delete g[e],(c.silent?this._silent: 12 | b)[e]=!0;c.unset?delete h[e]:h[e]=a;!f.isEqual(j[e],a)||f.has(h,e)!=f.has(j,e)?(this.changed[e]=a,c.silent||(this._pending[e]=!0)):(delete this.changed[e],delete this._pending[e])}c.silent||this.change(c);return this},unset:function(a,b){(b||(b={})).unset=!0;return this.set(a,null,b)},clear:function(a){(a||(a={})).unset=!0;return this.set(f.clone(this.attributes),a)},fetch:function(a){var a=a?f.clone(a):{},b=this,c=a.success;a.success=function(d,e,f){if(!b.set(b.parse(d,f),a))return!1;c&&c(b,d)}; 13 | a.error=g.wrapError(a.error,b,a);return(this.sync||g.sync).call(this,"read",this,a)},save:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c=c?f.clone(c):{};if(c.wait){if(!this._validate(d,c))return!1;e=f.clone(this.attributes)}a=f.extend({},c,{silent:!0});if(d&&!this.set(d,c.wait?a:c))return!1;var h=this,i=c.success;c.success=function(a,b,e){b=h.parse(a,e);if(c.wait){delete c.wait;b=f.extend(d||{},b)}if(!h.set(b,c))return false;i?i(h,a):h.trigger("sync",h,a,c)};c.error=g.wrapError(c.error, 14 | h,c);b=this.isNew()?"create":"update";b=(this.sync||g.sync).call(this,b,this,c);c.wait&&this.set(e,a);return b},destroy:function(a){var a=a?f.clone(a):{},b=this,c=a.success,d=function(){b.trigger("destroy",b,b.collection,a)};if(this.isNew())return d(),!1;a.success=function(e){a.wait&&d();c?c(b,e):b.trigger("sync",b,e,a)};a.error=g.wrapError(a.error,b,a);var e=(this.sync||g.sync).call(this,"delete",this,a);a.wait||d();return e},url:function(){var a=n(this,"urlRoot")||n(this.collection,"url")||t(); 15 | return this.isNew()?a:a+("/"==a.charAt(a.length-1)?"":"/")+encodeURIComponent(this.id)},parse:function(a){return a},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return null==this.id},change:function(a){a||(a={});var b=this._changing;this._changing=!0;for(var c in this._silent)this._pending[c]=!0;var d=f.extend({},a.changes,this._silent);this._silent={};for(c in d)this.trigger("change:"+c,this,this.get(c),a);if(b)return this;for(;!f.isEmpty(this._pending);){this._pending= 16 | {};this.trigger("change",this,a);for(c in this.changed)!this._pending[c]&&!this._silent[c]&&delete this.changed[c];this._previousAttributes=f.clone(this.attributes)}this._changing=!1;return this},hasChanged:function(a){return!arguments.length?!f.isEmpty(this.changed):f.has(this.changed,a)},changedAttributes:function(a){if(!a)return this.hasChanged()?f.clone(this.changed):!1;var b,c=!1,d=this._previousAttributes,e;for(e in a)if(!f.isEqual(d[e],b=a[e]))(c||(c={}))[e]=b;return c},previous:function(a){return!arguments.length|| 17 | !this._previousAttributes?null:this._previousAttributes[a]},previousAttributes:function(){return f.clone(this._previousAttributes)},isValid:function(){return!this.validate(this.attributes)},_validate:function(a,b){if(b.silent||!this.validate)return!0;var a=f.extend({},this.attributes,a),c=this.validate(a,b);if(!c)return!0;b&&b.error?b.error(this,c,b):this.trigger("error",this,c,b);return!1}});var r=g.Collection=function(a,b){b||(b={});b.model&&(this.model=b.model);b.comparator&&(this.comparator=b.comparator); 18 | this._reset();this.initialize.apply(this,arguments);a&&this.reset(a,{silent:!0,parse:b.parse})};f.extend(r.prototype,k,{model:o,initialize:function(){},toJSON:function(a){return this.map(function(b){return b.toJSON(a)})},add:function(a,b){var c,d,e,g,i,j={},k={},l=[];b||(b={});a=f.isArray(a)?a.slice():[a];c=0;for(d=a.length;c=b))this.iframe=i('