Proper exception management

This commit updates the service to throw dedicated exception types for
common user error and associates proper HTTP status codes to them.

Fixes gh-38
This commit is contained in:
Stephane Nicoll
2014-10-25 15:36:45 +02:00
parent 1136d76b1b
commit 7350cd165d
7 changed files with 94 additions and 10 deletions

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr
import groovy.transform.InheritConstructors
/**
* Base Initializr exception.
*
* @author Stephane Nicoll
* @since 1.0
*/
@InheritConstructors
class InitializrException extends RuntimeException {
}

View File

@@ -25,7 +25,6 @@ import groovy.transform.InheritConstructors
* @since 1.0
*/
@InheritConstructors
class InvalidInitializrMetadataException extends RuntimeException {
class InvalidInitializrMetadataException extends InitializrException {
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr
import groovy.transform.InheritConstructors
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.ResponseStatus
/**
* Thrown when a {@link ProjectRequest} is invalid.
*
* @author Stephane Nicoll
* @since 1.0
*/
@InheritConstructors
@ResponseStatus(HttpStatus.BAD_REQUEST)
class InvalidProjectRequestException extends InitializrException {
}

View File

@@ -57,7 +57,7 @@ class ProjectRequest {
def dependency = metadata.getDependency(it)
if (dependency == null) {
if (it.contains(':')) {
throw new IllegalArgumentException("Unknown dependency '$it' check project metadata")
throw new InvalidProjectRequestException("Unknown dependency '$it' check project metadata")
}
log.warn("No known dependency for style '$it' assuming spring-boot-starter")
dependency = new InitializrMetadata.Dependency()

View File

@@ -104,7 +104,7 @@ class MainController extends AbstractInitializrController {
upload(download, dir, generateFileName(request, 'zip'), 'application/zip')
}
@RequestMapping(value='/starter.tgz', produces='application/x-compress')
@RequestMapping(value = '/starter.tgz', produces = 'application/x-compress')
@ResponseBody
ResponseEntity<byte[]> springTgz(ProjectRequest request) {
def dir = projectGenerator.generateProjectStructure(request)
@@ -112,7 +112,7 @@ class MainController extends AbstractInitializrController {
def download = projectGenerator.createDistributionFile(dir, '.tgz')
new AntBuilder().tar(destfile: download, compression: 'gzip') {
zipfileset(dir:dir, includes:'**')
zipfileset(dir: dir, includes: '**')
}
upload(download, dir, generateFileName(request, 'tgz'), 'application/x-compress')
}

View File

@@ -73,7 +73,7 @@ class ProjectRequestTests {
request.style << 'org.foo:acme' // does not exist and
thrown.expect(IllegalArgumentException)
thrown.expect(InvalidProjectRequestException)
thrown.expectMessage('org.foo:acme')
request.resolve(metadata)
}

View File

@@ -30,6 +30,7 @@ import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.test.context.ActiveProfiles
import org.springframework.util.StreamUtils
import org.springframework.web.client.HttpClientErrorException
import static org.junit.Assert.*
@@ -39,6 +40,9 @@ import static org.junit.Assert.*
@ActiveProfiles('test-default')
class MainControllerIntegrationTests extends AbstractInitializrControllerIntegrationTests {
private final def slurper = new JsonSlurper()
@Test
void simpleZipProject() {
downloadZip('/starter.zip?style=web&style=jpa').isJavaProject().isMavenProject()
@@ -140,8 +144,23 @@ class MainControllerIntegrationTests extends AbstractInitializrControllerIntegra
}
private def metricsEndpoint() {
def slurper = new JsonSlurper()
slurper.parseText(restTemplate.getForObject(createUrl('/metrics'), String))
parseJson(restTemplate.getForObject(createUrl('/metrics'), String))
}
@Test
void missingDependencyProperException() {
try {
invoke('/starter.zip?style=foo:bar')
} catch (HttpClientErrorException ex) {
def error = parseJson(ex.responseBodyAsString)
assertEquals HttpStatus.BAD_REQUEST, ex.getStatusCode()
assertTrue 'Dependency not in error message', error.message.contains('foo:bar')
}
}
private def parseJson(String content) {
slurper.parseText(content)
}
@Test
@@ -212,13 +231,17 @@ class MainControllerIntegrationTests extends AbstractInitializrControllerIntegra
}
private byte[] invoke(String context) {
restTemplate.getForObject(createUrl(context), byte[])
}
private ProjectAssert downloadZip(String context) {
def body = restTemplate.getForObject(createUrl(context), byte[])
def body = invoke(context)
zipProjectAssert(body)
}
private ProjectAssert downloadTgz(String context) {
def body = restTemplate.getForObject(createUrl(context), byte[])
def body = invoke(context)
tgzProjectAssert(body)
}