[java-micronaut] Return HTTP 501 in default implementation (#12365)

The default controller implementation returns an empty response. This
might result in unexpected behavior when an operation isn't implemented,
as a consumer of the API there is no way to notice the difference
between an unimplemented method and an actual empty response.

By changing the default behavior to return HTTP 501 Not Implemented the
user will be made aware of unimplemented methods. The former default
behavior, returning an empty response by default, can be activated with
a configuration option.
This commit is contained in:
Auke Schrijnen 2022-05-18 08:17:22 +02:00 committed by GitHub
parent d38cb1b37a
commit a57509695a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 72 additions and 23 deletions

View File

@ -8,3 +8,5 @@ additionalProperties:
test: "spock"
requiredPropertiesInConstructor: "true"
useAuth: "false"
generateControllerAsAbstract: "false"
generateOperationsToReturnNotImplemented: "true"

View File

@ -44,6 +44,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|fullJavaUtil|whether to use fully qualified name for classes under java.util. This option only works for Java API client| |false|
|generateControllerAsAbstract|Generate an abstract class for controller to be extended. (apiPackage is then used for the abstract class, and controllerPackage is used for implementation that extends it.)| |false|
|generateControllerFromExamples|Generate the implementation of controller and tests from parameter and return examples that will verify that the api works as desired (for testing)| |false|
|generateOperationsToReturnNotImplemented|Return HTTP 501 Not Implemented instead of an empty response in the generated controller methods.| |true|
|groupId|groupId in generated pom.xml| |org.openapitools|
|hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |false|
|ignoreAnyOfInEnum|Ignore anyOf keyword in enum| |false|

View File

@ -12,7 +12,6 @@ import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@ -20,6 +19,7 @@ public class JavaMicronautServerCodegen extends JavaMicronautAbstractCodegen {
public static final String OPT_CONTROLLER_PACKAGE = "controllerPackage";
public static final String OPT_GENERATE_CONTROLLER_FROM_EXAMPLES = "generateControllerFromExamples";
public static final String OPT_GENERATE_CONTROLLER_AS_ABSTRACT = "generateControllerAsAbstract";
public static final String OPT_GENERATE_OPERATIONS_TO_RETURN_NOT_IMPLEMENTED = "generateOperationsToReturnNotImplemented";
public static final String EXTENSION_ROLES = "x-roles";
public static final String ANONYMOUS_ROLE_KEY = "isAnonymous()";
@ -35,6 +35,7 @@ public class JavaMicronautServerCodegen extends JavaMicronautAbstractCodegen {
protected String controllerPackage = "org.openapitools.controller";
protected boolean generateControllerAsAbstract = false;
protected boolean generateOperationsToReturnNotImplemented = true;
protected boolean generateControllerFromExamples = false;
protected boolean useAuth = true;
@ -65,6 +66,10 @@ public class JavaMicronautServerCodegen extends JavaMicronautAbstractCodegen {
" is then used for the abstract class, and " + OPT_CONTROLLER_PACKAGE +
" is used for implementation that extends it.)",
generateControllerAsAbstract));
cliOptions.add(CliOption.newBoolean(OPT_GENERATE_OPERATIONS_TO_RETURN_NOT_IMPLEMENTED,
"Return HTTP 501 Not Implemented instead of an empty response in the generated controller methods.",
generateOperationsToReturnNotImplemented));
cliOptions.add(CliOption.newBoolean(OPT_USE_AUTH, "Whether to import authorization and to annotate controller methods accordingly", useAuth));
// Set the type mappings
@ -96,6 +101,11 @@ public class JavaMicronautServerCodegen extends JavaMicronautAbstractCodegen {
}
writePropertyBack(OPT_GENERATE_CONTROLLER_AS_ABSTRACT, generateControllerAsAbstract);
if (additionalProperties.containsKey(OPT_GENERATE_OPERATIONS_TO_RETURN_NOT_IMPLEMENTED)) {
generateOperationsToReturnNotImplemented = convertPropertyToBoolean(OPT_GENERATE_OPERATIONS_TO_RETURN_NOT_IMPLEMENTED);
}
writePropertyBack(OPT_GENERATE_OPERATIONS_TO_RETURN_NOT_IMPLEMENTED, generateOperationsToReturnNotImplemented);
if (additionalProperties.containsKey(OPT_CONTROLLER_PACKAGE)) {
controllerPackage = (String) additionalProperties.get(OPT_CONTROLLER_PACKAGE);
} else if (!generateControllerAsAbstract && additionalProperties.containsKey(CodegenConstants.API_PACKAGE)) {

View File

@ -14,6 +14,10 @@ import reactor.core.publisher.Mono;
{{#wrapInHttpResponse}}
import io.micronaut.http.HttpResponse;
{{/wrapInHttpResponse}}
{{#generateOperationsToReturnNotImplemented}}
import io.micronaut.http.HttpStatus;
import io.micronaut.http.exceptions.HttpStatusException;
{{/generateOperationsToReturnNotImplemented}}
{{#imports}}
import {{import}};
{{/imports}}
@ -119,8 +123,9 @@ public {{#generateControllerAsAbstract}}abstract {{/generateControllerAsAbstract
*
* This method will be delegated to when the controller gets a request
*/
public abstract {{>common/operationReturnType}} {{nickname}}({{#allParams}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}});
{{/generateControllerAsAbstract}}
public {{^generateOperationsToReturnNotImplemented}}abstract {{/generateOperationsToReturnNotImplemented}}{{>common/operationReturnType}} {{nickname}}({{#allParams}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}){{^generateOperationsToReturnNotImplemented}};{{/generateOperationsToReturnNotImplemented}}{{#generateOperationsToReturnNotImplemented}} {{openbrace}}
{{>server/controllerOperationBody}}
{{closebrace}}{{/generateOperationsToReturnNotImplemented}}{{/generateControllerAsAbstract}}
{{^-last}}
{{/-last}}

View File

@ -1,7 +1,12 @@
{{^generateControllerFromExamples}}
{{!The body needs to be implemented by user}}
// TODO implement {{nickname}}();
{{^generateOperationsToReturnNotImplemented}}
{{#reactive}}{{#wrapInHttpResponse}}return Mono.fromCallable(HttpResponse::ok);{{/wrapInHttpResponse}}{{^wrapInHttpResponse}}return Mono.empty();{{/wrapInHttpResponse}}{{/reactive}}{{^reactive}}{{#wrapInHttpResponse}}return HttpResponse.ok();{{/wrapInHttpResponse}}{{^wrapInHttpResponse}}{{#returnType}}return null;{{/returnType}}{{/wrapInHttpResponse}}{{/reactive}}
{{/generateOperationsToReturnNotImplemented}}
{{#generateOperationsToReturnNotImplemented}}
{{#reactive}}{{#wrapInHttpResponse}}return Mono.just(HttpResponse.status(HttpStatus.NOT_IMPLEMENTED));{{/wrapInHttpResponse}}{{^wrapInHttpResponse}}return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));{{/wrapInHttpResponse}}{{/reactive}}{{^reactive}}{{#wrapInHttpResponse}}return HttpResponse.status(HttpStatus.NOT_IMPLEMENTED);{{/wrapInHttpResponse}}{{^wrapInHttpResponse}}throw new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null);{{/wrapInHttpResponse}}{{/reactive}}
{{/generateOperationsToReturnNotImplemented}}
{{/generateControllerFromExamples}}
{{#generateControllerFromExamples}}
{{!The body is generated to verify that example values are passed correctly}}

View File

@ -16,6 +16,8 @@ import io.micronaut.http.annotation.*;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.convert.format.Format;
import reactor.core.publisher.Mono;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.exceptions.HttpStatusException;
import io.micronaut.http.multipart.CompletedFileUpload;
import org.openapitools.model.ModelApiResponse;
import org.openapitools.model.Pet;
@ -60,9 +62,10 @@ public class PetController {
@Body @NotNull @Valid Pet pet
) {
// TODO implement addPet();
return Mono.empty();
return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
}
/**
* Deletes a pet
*
@ -90,9 +93,10 @@ public class PetController {
@Header(value="api_key") @Nullable String apiKey
) {
// TODO implement deletePet();
return Mono.empty();
return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
}
/**
* Finds Pets by status
* Multiple status values can be provided with comma separated strings
@ -121,9 +125,10 @@ public class PetController {
@QueryValue(value="status") @NotNull List<String> status
) {
// TODO implement findPetsByStatus();
return Mono.empty();
return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
}
/**
* Finds Pets by tags
* Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.
@ -152,9 +157,10 @@ public class PetController {
@QueryValue(value="tags") @NotNull List<String> tags
) {
// TODO implement findPetsByTags();
return Mono.empty();
return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
}
/**
* Find pet by ID
* Returns a single pet
@ -181,9 +187,10 @@ public class PetController {
@PathVariable(value="petId") @NotNull Long petId
) {
// TODO implement getPetById();
return Mono.empty();
return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
}
/**
* Update an existing pet
*
@ -215,9 +222,10 @@ public class PetController {
@Body @NotNull @Valid Pet pet
) {
// TODO implement updatePet();
return Mono.empty();
return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
}
/**
* Updates a pet in the store with form data
*
@ -248,9 +256,10 @@ public class PetController {
@Nullable String status
) {
// TODO implement updatePetWithForm();
return Mono.empty();
return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
}
/**
* uploads an image
*
@ -283,6 +292,7 @@ public class PetController {
@Nullable CompletedFileUpload _file
) {
// TODO implement uploadFile();
return Mono.empty();
return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
}
}

View File

@ -16,6 +16,8 @@ import io.micronaut.http.annotation.*;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.convert.format.Format;
import reactor.core.publisher.Mono;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.exceptions.HttpStatusException;
import org.openapitools.model.Order;
import javax.annotation.Generated;
import java.util.ArrayList;
@ -50,9 +52,10 @@ public class StoreController {
@PathVariable(value="orderId") @NotNull String orderId
) {
// TODO implement deleteOrder();
return Mono.empty();
return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
}
/**
* Returns pet inventories by status
* Returns a map of status codes to quantities
@ -75,9 +78,10 @@ public class StoreController {
@Produces(value = {"application/json"})
public Mono<Map<String, Integer>> getInventory() {
// TODO implement getInventory();
return Mono.empty();
return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
}
/**
* Find purchase order by ID
* For valid response try integer IDs with value &lt;&#x3D; 5 or &gt; 10. Other values will generated exceptions
@ -102,9 +106,10 @@ public class StoreController {
@PathVariable(value="orderId") @NotNull @Min(1L) @Max(5L) Long orderId
) {
// TODO implement getOrderById();
return Mono.empty();
return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
}
/**
* Place an order for a pet
*
@ -129,6 +134,7 @@ public class StoreController {
@Body @NotNull @Valid Order order
) {
// TODO implement placeOrder();
return Mono.empty();
return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
}
}

View File

@ -16,6 +16,8 @@ import io.micronaut.http.annotation.*;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.convert.format.Format;
import reactor.core.publisher.Mono;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.exceptions.HttpStatusException;
import java.time.OffsetDateTime;
import org.openapitools.model.User;
import javax.annotation.Generated;
@ -53,9 +55,10 @@ public class UserController {
@Body @NotNull @Valid User user
) {
// TODO implement createUser();
return Mono.empty();
return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
}
/**
* Creates list of users with given input array
*
@ -79,9 +82,10 @@ public class UserController {
@Body @NotNull List<User> user
) {
// TODO implement createUsersWithArrayInput();
return Mono.empty();
return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
}
/**
* Creates list of users with given input array
*
@ -105,9 +109,10 @@ public class UserController {
@Body @NotNull List<User> user
) {
// TODO implement createUsersWithListInput();
return Mono.empty();
return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
}
/**
* Delete user
* This can only be done by the logged in user.
@ -131,9 +136,10 @@ public class UserController {
@PathVariable(value="username") @NotNull String username
) {
// TODO implement deleteUser();
return Mono.empty();
return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
}
/**
* Get user by user name
*
@ -158,9 +164,10 @@ public class UserController {
@PathVariable(value="username") @NotNull String username
) {
// TODO implement getUserByName();
return Mono.empty();
return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
}
/**
* Logs user into the system
*
@ -186,9 +193,10 @@ public class UserController {
@QueryValue(value="password") @NotNull String password
) {
// TODO implement loginUser();
return Mono.empty();
return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
}
/**
* Logs out current logged in user session
*
@ -208,9 +216,10 @@ public class UserController {
@Produces(value = {})
public Mono<Void> logoutUser() {
// TODO implement logoutUser();
return Mono.empty();
return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
}
/**
* Updated user
* This can only be done by the logged in user.
@ -237,6 +246,7 @@ public class UserController {
@Body @NotNull @Valid User user
) {
// TODO implement updateUser();
return Mono.empty();
return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
}
}