Skip to content

Commit 950ece5

Browse files
committed
BAPI-15281: restful-api changes to support additional use cases with semantic versioning
1 parent 49f8069 commit 950ece5

File tree

5 files changed

+108
-56
lines changed

5 files changed

+108
-56
lines changed

README.md

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!-- ***************************************************************************
2-
* Copyright 2013-2018 Ellucian Company L.P. and its affiliates.
2+
* Copyright 2013-2019 Ellucian Company L.P. and its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -48,7 +48,7 @@ The restful-api plugin is designed to facilitate exposing RESTful API endpoints
4848
This plugin should be installed from the official Grails Central Plugin Repository ([http://grails.org/plugins/restful-api](http://grails.org/plugins/restful-api)) by setting the following dependency:
4949

5050
```
51-
compile ":restful-api:1.7.0"
51+
compile ":restful-api:1.8.0"
5252
```
5353

5454
_Note: It may sometimes be useful to install this plugin as a Git submodule instead (e.g., if you are actively contributing to the plugin). To add the plugin as a Git submodule under a 'plugins' directory:_
@@ -68,7 +68,7 @@ _Then add the in-place plugin definition to BuildConfig.groovy:_
6868
_Adding the plugin this way will use the latest commit on the master branch at the time you ran the submodule command. If you want to use an official release instead, go to the plugin directory and checkout a specific version, e.g.:_
6969

7070
cd plugins/restful-api.git
71-
git checkout 1.7.0
71+
git checkout 1.8.0
7272

7373
_Lastly, don't forget to go back to your project root and commit the change this will make to your git submodules file._
7474

@@ -3465,14 +3465,14 @@ the application. The following is an example:
34653465
restfulApi.overrideGenericMediaType = true
34663466
restfulApi.genericMediaTypeList = ["application/json", "application/vnd.hedtech.integration+json"]
34673467

3468-
###Override version range media type for semantic versions
3469-
To override the whole digit representation version number with the highest semantic version where the major version
3470-
matches, you must set restfulApi.overrideVersionRangeMediaType=true in Config.groovy. The following is an example:
3468+
###Use highest semantic version for media type
3469+
To dynamically replace all versioned media types with the highest semantic version where the major version matches,
3470+
you must set restfulApi.useHighestSemanticVersion=true in Config.groovy. The following is an example:
34713471

3472-
restfulApi.overrideVersionRangeMediaType = true
3472+
restfulApi.useHighestSemanticVersion = true
34733473

3474-
This is in support of version ranges that facilitate easier caller adoption of non-breaking API changes. This
3475-
feature requires an ApiVersionParser to be configured.
3474+
This is to facilitate easier caller adoption of non-breaking API changes. This feature requires an ApiVersionParser
3475+
to be configured.
34763476

34773477
An example of this using a representation with multiple media types. Given a representation of
34783478
* application/vnd.hedtech.integration.v6+json
@@ -3484,7 +3484,11 @@ An example of this using a representation with multiple media types. Given a rep
34843484
* application/vnd.hedtech.integration.v8+json
34853485
* application/vnd.hedtech.integration.v8.0.0+json
34863486

3487-
This feature will associate application/vnd.hedtech.integration.v7+json with v7.2.1 as it is the highest semantic version for the given representation having a major version of v7. Callers requesting v7, will automatically be returned the highest representation as it is non-breaking by semantic definition. Also, application/vnd.hedtech.integration.v8+json will be associated with v8.0.0. Lastly, application/vnd.hedtech.integration.v6+json will remain as a non-semantic version associated with v6.
3487+
This feature will associate application/vnd.hedtech.integration.v7+json, v7.0.0+json, v7.1.0+json, v7.2.0+json with v7.2.1 as it is the highest semantic version for the given representation having a major version of v7. Callers requesting any v7 version, will automatically be returned the highest representation as it is non-breaking by semantic definition. Also, application/vnd.hedtech.integration.v8+json will be associated with v8.0.0. Lastly, application/vnd.hedtech.integration.v6+json will remain as a non-semantic version associated with v6.
3488+
3489+
Some callers may not be able to adopt this change being reflected in X-Media-Type right away. Since minor version upgrades are non-breaking, the framework allows callers to delay transitioning to full semantic versioning of the X-Media-Type response header. This is accomplished by setting a configuration property such that whatever is being requested in the Accept request header will be returned in the response X-Media-Type header. To implement this behaviour, specify in Config.groovy the following:
3490+
3491+
restfulApi.useAcceptHeaderAsMediaTypeHeader = true
34883492

34893493
##Reporting and discovery of all configured resources
34903494
The RestfulApiController exposes selected information about all resources that have been configured through an optional ResourceDetailList. Simply configure the Spring application context for the supplied ResourceDetailList class as a bean named resourceDetailList:

RELEASE-NOTES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
#1.8.0
2+
* Replace overrideVersionRangeMediaType=true setting with useHighestSemanticVersion=true to dynamically replace all versioned media types with the highest semantic version where the major version matches. This is to facilitate easier caller adoption of non-breaking API changes. This feature requires an ApiVersionParser to be configured.
3+
* Add setting useAcceptHeaderAsMediaTypeHeader=true in Config.groovy to return the Accept request header as the X-Media-Type response header for some callers to delay transitioning to full semantic versioning of the X-Media-Type response header.
4+
15
#1.7.0
26
* Add setting overrideVersionRangeMediaType=true to allow override of a whole digit representation version number with the highest semantic version where the major version matches. This is in support of version ranges that facilitate easier caller adoption of non-breaking API changes. This feature requires an ApiVersionParser to be configured.
37
* Correct extensibility to not execute WRITE SQL if the request is a POST /qapi request (this represents a query by post read-only request).

RestfulApiGrailsPlugin.groovy

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* ****************************************************************************
2-
* Copyright 2013-2018 Ellucian Company L.P. and its affiliates.
2+
* Copyright 2013-2019 Ellucian Company L.P. and its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -24,7 +24,7 @@ import org.codehaus.groovy.grails.web.converters.configuration.DefaultConverterC
2424

2525
class RestfulApiGrailsPlugin {
2626

27-
def version = "1.7.0"
27+
def version = "1.8.0"
2828
def grailsVersion = "2.2.0 > *"
2929
def pluginExcludes = [
3030
"grails-app/views/**",

grails-app/controllers/net/hedtech/restfulapi/RestfulApiController.groovy

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* ***************************************************************************
2-
* Copyright 2013-2018 Ellucian Company L.P. and its affiliates.
2+
* Copyright 2013-2019 Ellucian Company L.P. and its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -131,12 +131,16 @@ class RestfulApiController {
131131
// Setting overrideGenericMediaType=true in Config.groovy will replace generic media
132132
// types with latest actual versioned media types for a representation (where available).
133133
// The generic media types also need to be configured in genericMediaTypeList property.
134-
// Setting overrideVersionRangeMediaType=true in Config.groovy will override the whole digit
135-
// version number with the highest semantic version where the major version matches.
134+
// Setting useHighestSemanticVersion=true in Config.groovy will dynamically replace all
135+
// versioned media types with the highest semantic version where the major version matches.
136+
// Setting useAcceptHeaderAsMediaTypeHeader=true in Config.groovy will return
137+
// the Accept request header as the X-Media-Type response header for some clients to
138+
// delay transitioning to full semantic versioning of the X-Media-Type response header.
136139
ApiVersionParser apiVersionParser
137140
boolean overrideGenericMediaType
138141
List genericMediaTypeList
139-
boolean overrideVersionRangeMediaType
142+
boolean useHighestSemanticVersion
143+
boolean useAcceptHeaderAsMediaTypeHeader
140144

141145
private Class pagedResultListClazz
142146

@@ -181,7 +185,8 @@ class RestfulApiController {
181185

182186
overrideGenericMediaType = getOverrideGenericMediaType()
183187
genericMediaTypeList = getGenericMediaTypeList()
184-
overrideVersionRangeMediaType = getOverrideVersionRangeMediaType()
188+
useHighestSemanticVersion = getUseHighestSemanticVersion()
189+
useAcceptHeaderAsMediaTypeHeader = getUseAcceptHeaderAsMediaTypeHeader()
185190

186191
JSON.createNamedConfig('restapi-error:json') { }
187192
XML.createNamedConfig('restapi-error:xml') { }
@@ -228,13 +233,13 @@ class RestfulApiController {
228233
}
229234
}
230235
}
231-
if (overrideVersionRangeMediaType && representation.apiVersion.version?.indexOf('.') == -1) {
236+
if (useHighestSemanticVersion && representation.apiVersion.version) {
232237
if (representation.allMediaTypes.size() > 1 && !genericMediaTypeList.contains(representation.mediaType)) {
233238
List apiVersionList = []
234239
representation.allMediaTypes.each { mediaType ->
235240
if (!genericMediaTypeList.contains(mediaType)) {
236241
def testApiVersion = apiVersionParser.parseMediaType(resource.name, mediaType)
237-
if (testApiVersion.version?.startsWith(representation.apiVersion.version+".")) {
242+
if (testApiVersion.majorVersion == representation.apiVersion.majorVersion) {
238243
apiVersionList.add(testApiVersion)
239244
}
240245
}
@@ -723,7 +728,7 @@ class RestfulApiController {
723728
String responseMediaType = representation.mediaType
724729
String apiVersionMediaType = representation.apiVersion?.mediaType
725730
if (apiVersionMediaType) {
726-
if (overrideVersionRangeMediaType ||
731+
if ((useHighestSemanticVersion && !useAcceptHeaderAsMediaTypeHeader) ||
727732
(overrideGenericMediaType && genericMediaTypeList.contains(responseMediaType))) {
728733
responseMediaType = apiVersionMediaType
729734
}
@@ -1085,8 +1090,14 @@ class RestfulApiController {
10851090
}
10861091

10871092

1088-
private boolean getOverrideVersionRangeMediaType() {
1089-
def value = grailsApplication.config.restfulApi.overrideVersionRangeMediaType
1093+
private boolean getUseHighestSemanticVersion() {
1094+
def value = grailsApplication.config.restfulApi.useHighestSemanticVersion
1095+
(value instanceof Boolean) ? value : false
1096+
}
1097+
1098+
1099+
private boolean getUseAcceptHeaderAsMediaTypeHeader() {
1100+
def value = grailsApplication.config.restfulApi.useAcceptHeaderAsMediaTypeHeader
10901101
(value instanceof Boolean) ? value : false
10911102
}
10921103

0 commit comments

Comments
 (0)