Skip to content

Commit cccc691

Browse files
committed
Merge branch 'bug/6/fix-rendering'
This fixes generation of spurious grails/gorm properties not needed for the actual service. The convention is introduced to support restful services. For regular web endpoints the preferred route is to use swagger annotations. fixes #6
2 parents e8e07ca + 53584f9 commit cccc691

File tree

49 files changed

+1346
-691
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1346
-691
lines changed

build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ allprojects {
1515
}
1616

1717
ext {
18+
byteBuddyVersion = "1.6.10"
1819
cglib = "3.2.4"
1920
classmate = "1.3.3"
2021
groovy = "2.4.8"
@@ -35,6 +36,6 @@ allprojects {
3536
springPluginVersion = "1.2.0.RELEASE"
3637
swagger2Core = "1.5.12"
3738
springBoot = "1.4.4.RELEASE"
38-
springfox = "2.6.1"
39+
springfox = "2.7.0-SNAPSHOT"
3940
}
4041
}

springfox-grails-contract-tests/build.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,13 @@ dependencies {
5858
compile "org.grails.plugins:views-json"
5959
compile "org.grails.plugins:views-json-templates"
6060
console "org.grails:grails-console"
61+
6162
profile "org.grails.profiles:rest-api"
63+
6264
provided "org.codehaus.groovy:groovy-ant"
65+
6366
runtime "com.h2database:h2"
67+
6468
testCompile "org.grails:grails-plugin-testing"
6569
testCompile "org.grails.plugins:geb"
6670
testCompile "org.grails:grails-datastore-rest-client"

springfox-grails-contract-tests/grails-app/controllers/grails/springfox/sample/AlbumController.groovy

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
package grails.springfox.sample
22

3-
4-
import grails.rest.*
5-
import grails.converters.*
3+
import grails.rest.RestfulController
64

75
class AlbumController extends RestfulController {
6+
static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]
87
static responseFormats = ['json', 'xml']
98
AlbumController() {
109
super(Album)
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package grails.springfox.sample
2+
3+
import grails.transaction.Transactional
4+
import io.swagger.annotations.ApiImplicitParam
5+
import io.swagger.annotations.ApiImplicitParams
6+
import io.swagger.annotations.ApiOperation
7+
import springfox.documentation.annotations.ApiIgnore
8+
9+
import static org.springframework.http.HttpStatus.*
10+
11+
@Transactional(readOnly = true)
12+
class BookController {
13+
14+
static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]
15+
16+
@ApiOperation(value = "index", httpMethod = "GET", notes="Creates a book")
17+
def index(Integer max) {
18+
params.max = Math.min(max ?: 10, 100)
19+
respond Book.list(params), model:[bookCount: Book.count()]
20+
}
21+
22+
@ApiIgnore
23+
def show(Book book) {
24+
respond book
25+
}
26+
27+
@ApiOperation(value = "create", httpMethod = "POST", notes="Creates a book")
28+
@ApiImplicitParams([
29+
@ApiImplicitParam(name = "name", dataType = "string", required = true, paramType = "form",
30+
value = "Name of the book")
31+
])
32+
def create() {
33+
respond new Book(params)
34+
}
35+
36+
@Transactional
37+
@ApiOperation(value = "save", httpMethod = "POST", notes="Saves a book")
38+
def save(Book book) {
39+
if (book == null) {
40+
transactionStatus.setRollbackOnly()
41+
notFound()
42+
return
43+
}
44+
45+
if (book.hasErrors()) {
46+
transactionStatus.setRollbackOnly()
47+
respond book.errors, view:'create'
48+
return
49+
}
50+
51+
book.save flush:true
52+
53+
request.withFormat {
54+
form multipartForm {
55+
flash.message = message(code: 'default.created.message', args: [message(code: 'book.label', default: 'Book'), book.id])
56+
redirect book
57+
}
58+
'*' { respond book, [status: CREATED] }
59+
}
60+
}
61+
62+
@ApiIgnore
63+
def edit(Book book) {
64+
respond book
65+
}
66+
67+
@Transactional
68+
@ApiOperation(value = "update", httpMethod = "PUT", notes="Updates a book")
69+
def update(Book book) {
70+
if (book == null) {
71+
transactionStatus.setRollbackOnly()
72+
notFound()
73+
return
74+
}
75+
76+
if (book.hasErrors()) {
77+
transactionStatus.setRollbackOnly()
78+
respond book.errors, view:'edit'
79+
return
80+
}
81+
82+
book.save flush:true
83+
84+
request.withFormat {
85+
form multipartForm {
86+
flash.message = message(code: 'default.updated.message', args: [message(code: 'book.label', default: 'Book'), book.id])
87+
redirect book
88+
}
89+
'*'{ respond book, [status: OK] }
90+
}
91+
}
92+
93+
@Transactional
94+
@ApiOperation(value = "delete", httpMethod = "DELETE", notes="Deletes a book")
95+
@ApiImplicitParams([
96+
@ApiImplicitParam(name = "id", dataType = "long", required = true, paramType = "form",
97+
value = "Id of the book")
98+
])
99+
def delete(Book book) {
100+
101+
if (book == null) {
102+
transactionStatus.setRollbackOnly()
103+
notFound()
104+
return
105+
}
106+
107+
book.delete flush:true
108+
109+
request.withFormat {
110+
form multipartForm {
111+
flash.message = message(code: 'default.deleted.message', args: [message(code: 'book.label', default: 'Book'), book.id])
112+
redirect action:"index", method:"GET"
113+
}
114+
'*'{ render status: NO_CONTENT }
115+
}
116+
}
117+
118+
protected void notFound() {
119+
request.withFormat {
120+
form multipartForm {
121+
flash.message = message(code: 'default.not.found.message', args: [message(code: 'book.label', default: 'Book'), params.id])
122+
redirect action: "index", method: "GET"
123+
}
124+
'*'{ render status: NOT_FOUND }
125+
}
126+
}
127+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package grails.springfox.sample
2+
3+
class Book {
4+
Long id
5+
String name
6+
7+
static constraints = {
8+
name nullable: false
9+
}
10+
}

springfox-grails-contract-tests/grails-app/init/grails/springfox/sample/Application.groovy

Lines changed: 58 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,50 +2,79 @@ package grails.springfox.sample
22

33
import grails.boot.GrailsApp
44
import grails.boot.config.GrailsAutoConfiguration
5+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
56
import org.springframework.context.annotation.Bean
67
import org.springframework.context.annotation.Import
78
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry
89
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
10+
import springfox.documentation.grails.AlternateTypeRuleConvention
11+
import springfox.documentation.grails.DefaultGrailsPropertySelector
12+
import springfox.documentation.grails.DefaultGrailsPropertyTransformer
13+
import springfox.documentation.grails.DefaultGeneratedClassNamingStrategy
14+
import springfox.documentation.grails.GrailsPropertySelector
15+
import springfox.documentation.grails.GrailsPropertyTransformer
16+
import springfox.documentation.grails.GeneratedClassNamingStrategy
917
import springfox.documentation.grails.SpringfoxGrailsIntegrationConfiguration
18+
import springfox.documentation.schema.AlternateTypeRule
1019
import springfox.documentation.spi.DocumentationType
1120
import springfox.documentation.spring.web.plugins.Docket
1221
import springfox.documentation.swagger2.annotations.EnableSwagger2
1322

14-
import static com.google.common.base.Predicates.not
15-
import static springfox.documentation.builders.PathSelectors.ant
23+
import static com.google.common.base.Predicates.*
24+
import static springfox.documentation.builders.PathSelectors.*
1625

1726
@EnableSwagger2
1827
@Import([SpringfoxGrailsIntegrationConfiguration])
1928
class Application extends GrailsAutoConfiguration {
20-
static void main(String[] args) {
21-
GrailsApp.run(Application, args)
22-
}
29+
static void main(String[] args) {
30+
GrailsApp.run(Application, args)
31+
}
2332

24-
@Bean
25-
Docket api() {
26-
new Docket(DocumentationType.SWAGGER_2)
27-
.ignoredParameterTypes(MetaClass)
28-
.select()
29-
.paths(not(ant("/error")))
30-
.build()
31-
}
33+
@Bean
34+
@ConditionalOnMissingBean(GrailsPropertySelector)
35+
GrailsPropertySelector propertySelector() {
36+
new DefaultGrailsPropertySelector()
37+
}
38+
39+
@Bean
40+
@ConditionalOnMissingBean(GrailsPropertyTransformer)
41+
GrailsPropertyTransformer propertyTransformer() {
42+
new DefaultGrailsPropertyTransformer()
43+
}
3244

33-
@Bean
34-
static WebMvcConfigurerAdapter webConfigurer() {
35-
new WebMvcConfigurerAdapter() {
36-
@Override
37-
void addResourceHandlers(ResourceHandlerRegistry registry) {
38-
if (!registry.hasMappingForPattern("/webjars/**")) {
39-
registry
40-
.addResourceHandler("/webjars/**")
41-
.addResourceLocations("classpath:/META-INF/resources/webjars/")
42-
}
43-
if (!registry.hasMappingForPattern("/swagger-ui.html")) {
44-
registry
45-
.addResourceHandler("/swagger-ui.html")
46-
.addResourceLocations("classpath:/META-INF/resources/swagger-ui.html")
47-
}
48-
}
45+
@Bean
46+
@ConditionalOnMissingBean(GeneratedClassNamingStrategy)
47+
GeneratedClassNamingStrategy namingStrategy() {
48+
new DefaultGeneratedClassNamingStrategy()
49+
}
50+
51+
@Bean
52+
Docket api(AlternateTypeRuleConvention convention) {
53+
def typeRules = convention.rules()
54+
new Docket(DocumentationType.SWAGGER_2)
55+
.ignoredParameterTypes(MetaClass)
56+
.select()
57+
.paths(not(ant("/error")))
58+
.build()
59+
.alternateTypeRules(typeRules.toArray(new AlternateTypeRule[typeRules.size()]))
60+
}
61+
62+
@Bean
63+
static WebMvcConfigurerAdapter webConfigurer() {
64+
new WebMvcConfigurerAdapter() {
65+
@Override
66+
void addResourceHandlers(ResourceHandlerRegistry registry) {
67+
if (!registry.hasMappingForPattern("/webjars/**")) {
68+
registry
69+
.addResourceHandler("/webjars/**")
70+
.addResourceLocations("classpath:/META-INF/resources/webjars/")
71+
}
72+
if (!registry.hasMappingForPattern("/swagger-ui.html")) {
73+
registry
74+
.addResourceHandler("/swagger-ui.html")
75+
.addResourceLocations("classpath:/META-INF/resources/swagger-ui.html")
4976
}
77+
}
5078
}
79+
}
5180
}

springfox-grails-contract-tests/src/integration-test/groovy/grails/springfox/sample/SpringFoxSpec.groovy

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,22 @@ import static io.restassured.RestAssured.get
1212

1313
@Integration
1414
class SpringFoxSpec extends Specification implements FileAccess {
15-
@LocalServerPort
16-
private int port
17-
void "test something"() {
18-
given:
19-
def expected = fileContents("/expected-service-description.json")
20-
def actual = get("http://localhost:$port/v2/api-docs").asString()
15+
@LocalServerPort
16+
private int port
2117

22-
expect:
23-
try {
24-
JSONAssert.assertEquals(
25-
expected.replaceAll("__PORT__", "$port"),
26-
actual,
27-
JSONCompareMode.NON_EXTENSIBLE)
28-
} catch (AssertionError e) {
29-
Assert.fail("${e.getMessage()}${System.getProperty("line.separator")}${JsonOutput.prettyPrint(actual)}")
30-
}
18+
void "test something"() {
19+
given:
20+
def expected = fileContents("/expected-service-description.json")
21+
def actual = get("http://localhost:$port/v2/api-docs").asString()
3122

32-
33-
}
23+
expect:
24+
try {
25+
JSONAssert.assertEquals(
26+
expected.replaceAll("__PORT__", "$port"),
27+
actual,
28+
JSONCompareMode.NON_EXTENSIBLE)
29+
} catch (AssertionError e) {
30+
Assert.fail("${e.getMessage()}${System.getProperty("line.separator")}${JsonOutput.prettyPrint(actual)}")
31+
}
32+
}
3433
}

0 commit comments

Comments
 (0)