[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" test: "spock"
requiredPropertiesInConstructor: "true" requiredPropertiesInConstructor: "true"
useAuth: "false" 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| |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| |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| |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| |groupId|groupId in generated pom.xml| |org.openapitools|
|hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |false| |hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |false|
|ignoreAnyOfInEnum|Ignore anyOf keyword in enum| |false| |ignoreAnyOfInEnum|Ignore anyOf keyword in enum| |false|

View File

@ -12,7 +12,6 @@ import org.slf4j.LoggerFactory;
import java.io.File; import java.io.File;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; 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_CONTROLLER_PACKAGE = "controllerPackage";
public static final String OPT_GENERATE_CONTROLLER_FROM_EXAMPLES = "generateControllerFromExamples"; 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_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 EXTENSION_ROLES = "x-roles";
public static final String ANONYMOUS_ROLE_KEY = "isAnonymous()"; public static final String ANONYMOUS_ROLE_KEY = "isAnonymous()";
@ -35,6 +35,7 @@ public class JavaMicronautServerCodegen extends JavaMicronautAbstractCodegen {
protected String controllerPackage = "org.openapitools.controller"; protected String controllerPackage = "org.openapitools.controller";
protected boolean generateControllerAsAbstract = false; protected boolean generateControllerAsAbstract = false;
protected boolean generateOperationsToReturnNotImplemented = true;
protected boolean generateControllerFromExamples = false; protected boolean generateControllerFromExamples = false;
protected boolean useAuth = true; 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 then used for the abstract class, and " + OPT_CONTROLLER_PACKAGE +
" is used for implementation that extends it.)", " is used for implementation that extends it.)",
generateControllerAsAbstract)); 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)); cliOptions.add(CliOption.newBoolean(OPT_USE_AUTH, "Whether to import authorization and to annotate controller methods accordingly", useAuth));
// Set the type mappings // Set the type mappings
@ -96,6 +101,11 @@ public class JavaMicronautServerCodegen extends JavaMicronautAbstractCodegen {
} }
writePropertyBack(OPT_GENERATE_CONTROLLER_AS_ABSTRACT, generateControllerAsAbstract); 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)) { if (additionalProperties.containsKey(OPT_CONTROLLER_PACKAGE)) {
controllerPackage = (String) additionalProperties.get(OPT_CONTROLLER_PACKAGE); controllerPackage = (String) additionalProperties.get(OPT_CONTROLLER_PACKAGE);
} else if (!generateControllerAsAbstract && additionalProperties.containsKey(CodegenConstants.API_PACKAGE)) { } else if (!generateControllerAsAbstract && additionalProperties.containsKey(CodegenConstants.API_PACKAGE)) {

View File

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

View File

@ -1,7 +1,12 @@
{{^generateControllerFromExamples}} {{^generateControllerFromExamples}}
{{!The body needs to be implemented by user}} {{!The body needs to be implemented by user}}
// TODO implement {{nickname}}(); // 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}} {{#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}}
{{#generateControllerFromExamples}} {{#generateControllerFromExamples}}
{{!The body is generated to verify that example values are passed correctly}} {{!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.annotation.Nullable;
import io.micronaut.core.convert.format.Format; import io.micronaut.core.convert.format.Format;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.exceptions.HttpStatusException;
import io.micronaut.http.multipart.CompletedFileUpload; import io.micronaut.http.multipart.CompletedFileUpload;
import org.openapitools.model.ModelApiResponse; import org.openapitools.model.ModelApiResponse;
import org.openapitools.model.Pet; import org.openapitools.model.Pet;
@ -60,9 +62,10 @@ public class PetController {
@Body @NotNull @Valid Pet pet @Body @NotNull @Valid Pet pet
) { ) {
// TODO implement addPet(); // TODO implement addPet();
return Mono.empty(); return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
} }
/** /**
* Deletes a pet * Deletes a pet
* *
@ -90,9 +93,10 @@ public class PetController {
@Header(value="api_key") @Nullable String apiKey @Header(value="api_key") @Nullable String apiKey
) { ) {
// TODO implement deletePet(); // TODO implement deletePet();
return Mono.empty(); return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
} }
/** /**
* Finds Pets by status * Finds Pets by status
* Multiple status values can be provided with comma separated strings * Multiple status values can be provided with comma separated strings
@ -121,9 +125,10 @@ public class PetController {
@QueryValue(value="status") @NotNull List<String> status @QueryValue(value="status") @NotNull List<String> status
) { ) {
// TODO implement findPetsByStatus(); // TODO implement findPetsByStatus();
return Mono.empty(); return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
} }
/** /**
* Finds Pets by tags * Finds Pets by tags
* Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. * 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 @QueryValue(value="tags") @NotNull List<String> tags
) { ) {
// TODO implement findPetsByTags(); // TODO implement findPetsByTags();
return Mono.empty(); return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
} }
/** /**
* Find pet by ID * Find pet by ID
* Returns a single pet * Returns a single pet
@ -181,9 +187,10 @@ public class PetController {
@PathVariable(value="petId") @NotNull Long petId @PathVariable(value="petId") @NotNull Long petId
) { ) {
// TODO implement getPetById(); // TODO implement getPetById();
return Mono.empty(); return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
} }
/** /**
* Update an existing pet * Update an existing pet
* *
@ -215,9 +222,10 @@ public class PetController {
@Body @NotNull @Valid Pet pet @Body @NotNull @Valid Pet pet
) { ) {
// TODO implement updatePet(); // TODO implement updatePet();
return Mono.empty(); return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
} }
/** /**
* Updates a pet in the store with form data * Updates a pet in the store with form data
* *
@ -248,9 +256,10 @@ public class PetController {
@Nullable String status @Nullable String status
) { ) {
// TODO implement updatePetWithForm(); // TODO implement updatePetWithForm();
return Mono.empty(); return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
} }
/** /**
* uploads an image * uploads an image
* *
@ -283,6 +292,7 @@ public class PetController {
@Nullable CompletedFileUpload _file @Nullable CompletedFileUpload _file
) { ) {
// TODO implement uploadFile(); // 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.annotation.Nullable;
import io.micronaut.core.convert.format.Format; import io.micronaut.core.convert.format.Format;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.exceptions.HttpStatusException;
import org.openapitools.model.Order; import org.openapitools.model.Order;
import javax.annotation.Generated; import javax.annotation.Generated;
import java.util.ArrayList; import java.util.ArrayList;
@ -50,9 +52,10 @@ public class StoreController {
@PathVariable(value="orderId") @NotNull String orderId @PathVariable(value="orderId") @NotNull String orderId
) { ) {
// TODO implement deleteOrder(); // TODO implement deleteOrder();
return Mono.empty(); return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
} }
/** /**
* Returns pet inventories by status * Returns pet inventories by status
* Returns a map of status codes to quantities * Returns a map of status codes to quantities
@ -75,9 +78,10 @@ public class StoreController {
@Produces(value = {"application/json"}) @Produces(value = {"application/json"})
public Mono<Map<String, Integer>> getInventory() { public Mono<Map<String, Integer>> getInventory() {
// TODO implement getInventory(); // TODO implement getInventory();
return Mono.empty(); return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
} }
/** /**
* Find purchase order by ID * Find purchase order by ID
* For valid response try integer IDs with value &lt;&#x3D; 5 or &gt; 10. Other values will generated exceptions * 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 @PathVariable(value="orderId") @NotNull @Min(1L) @Max(5L) Long orderId
) { ) {
// TODO implement getOrderById(); // TODO implement getOrderById();
return Mono.empty(); return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
} }
/** /**
* Place an order for a pet * Place an order for a pet
* *
@ -129,6 +134,7 @@ public class StoreController {
@Body @NotNull @Valid Order order @Body @NotNull @Valid Order order
) { ) {
// TODO implement placeOrder(); // 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.annotation.Nullable;
import io.micronaut.core.convert.format.Format; import io.micronaut.core.convert.format.Format;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.exceptions.HttpStatusException;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import org.openapitools.model.User; import org.openapitools.model.User;
import javax.annotation.Generated; import javax.annotation.Generated;
@ -53,9 +55,10 @@ public class UserController {
@Body @NotNull @Valid User user @Body @NotNull @Valid User user
) { ) {
// TODO implement createUser(); // TODO implement createUser();
return Mono.empty(); return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
} }
/** /**
* Creates list of users with given input array * Creates list of users with given input array
* *
@ -79,9 +82,10 @@ public class UserController {
@Body @NotNull List<User> user @Body @NotNull List<User> user
) { ) {
// TODO implement createUsersWithArrayInput(); // TODO implement createUsersWithArrayInput();
return Mono.empty(); return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
} }
/** /**
* Creates list of users with given input array * Creates list of users with given input array
* *
@ -105,9 +109,10 @@ public class UserController {
@Body @NotNull List<User> user @Body @NotNull List<User> user
) { ) {
// TODO implement createUsersWithListInput(); // TODO implement createUsersWithListInput();
return Mono.empty(); return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
} }
/** /**
* Delete user * Delete user
* This can only be done by the logged in user. * This can only be done by the logged in user.
@ -131,9 +136,10 @@ public class UserController {
@PathVariable(value="username") @NotNull String username @PathVariable(value="username") @NotNull String username
) { ) {
// TODO implement deleteUser(); // TODO implement deleteUser();
return Mono.empty(); return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
} }
/** /**
* Get user by user name * Get user by user name
* *
@ -158,9 +164,10 @@ public class UserController {
@PathVariable(value="username") @NotNull String username @PathVariable(value="username") @NotNull String username
) { ) {
// TODO implement getUserByName(); // TODO implement getUserByName();
return Mono.empty(); return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
} }
/** /**
* Logs user into the system * Logs user into the system
* *
@ -186,9 +193,10 @@ public class UserController {
@QueryValue(value="password") @NotNull String password @QueryValue(value="password") @NotNull String password
) { ) {
// TODO implement loginUser(); // TODO implement loginUser();
return Mono.empty(); return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
} }
/** /**
* Logs out current logged in user session * Logs out current logged in user session
* *
@ -208,9 +216,10 @@ public class UserController {
@Produces(value = {}) @Produces(value = {})
public Mono<Void> logoutUser() { public Mono<Void> logoutUser() {
// TODO implement logoutUser(); // TODO implement logoutUser();
return Mono.empty(); return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
} }
/** /**
* Updated user * Updated user
* This can only be done by the logged in user. * This can only be done by the logged in user.
@ -237,6 +246,7 @@ public class UserController {
@Body @NotNull @Valid User user @Body @NotNull @Valid User user
) { ) {
// TODO implement updateUser(); // TODO implement updateUser();
return Mono.empty(); return Mono.error(new HttpStatusException(HttpStatus.NOT_IMPLEMENTED, null));
} }
} }