From 57b3e3db3ea41f2e70410b5b0ab432b51bd25265 Mon Sep 17 00:00:00 2001 From: William Cheng Date: Mon, 29 Jan 2024 20:58:02 +0800 Subject: [PATCH 1/5] add rule to remove x-internal in normalizer --- .../openapitools/codegen/DefaultCodegen.java | 45 ++++++++++--------- .../codegen/OpenAPINormalizer.java | 40 +++++++++++++++-- .../codegen/OpenAPINormalizerTest.java | 22 ++++++++- ...nableKeepOnlyFirstTagInOperation_test.yaml | 8 ++++ 4 files changed, 89 insertions(+), 26 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 637727ebbecd..2f1ecb763a50 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -4507,11 +4507,6 @@ public CodegenOperation fromOperation(String path, if (operation == null) throw new RuntimeException("operation cannot be null in fromOperation"); - if (operation.getExtensions() != null && Boolean.TRUE.equals(operation.getExtensions().get("x-internal"))) { - LOGGER.info("Operation ({} {} - {}) not generated since x-internal is set to true", - httpMethod, path, operation.getOperationId()); - } - Map schemas = ModelUtils.getSchemas(this.openAPI); CodegenOperation op = CodegenModelFactory.newInstance(CodegenModelType.OPERATION); Set imports = new HashSet<>(); @@ -5083,25 +5078,31 @@ public CodegenCallback fromCallback(String name, Callback callback, List String method = p.getKey(); Operation op = p.getValue(); - boolean genId = op.getOperationId() == null; - if (genId) { - op.setOperationId(getOrGenerateOperationId(op, c.name + "_" + expression.replaceAll("\\{\\$.*}", ""), method)); - } + if (op.getExtensions() != null && Boolean.TRUE.equals(op.getExtensions().get("x-internal"))) { + // skip operation if x-internal sets to true + LOGGER.info("Operation ({} {} - {}) not generated since x-internal is set to true", + method, expression, op.getOperationId()); + } else { + boolean genId = op.getOperationId() == null; + if (genId) { + op.setOperationId(getOrGenerateOperationId(op, c.name + "_" + expression.replaceAll("\\{\\$.*}", ""), method)); + } - if (op.getExtensions() == null) { - op.setExtensions(new HashMap<>()); - } - // This extension will be removed later by `fromOperation()` as it is only needed here to - // distinguish between normal operations and callback requests - op.getExtensions().put("x-callback-request", true); - - CodegenOperation co = fromOperation(expression, method, op, servers); - if (genId) { - co.operationIdOriginal = null; - // legacy (see `fromOperation()`) - co.nickname = co.operationId; + if (op.getExtensions() == null) { + op.setExtensions(new HashMap<>()); + } + // This extension will be removed later by `fromOperation()` as it is only needed here to + // distinguish between normal operations and callback requests + op.getExtensions().put("x-callback-request", true); + + CodegenOperation co = fromOperation(expression, method, op, servers); + if (genId) { + co.operationIdOriginal = null; + // legacy (see `fromOperation()`) + co.nickname = co.operationId; + } + u.requests.add(co); } - u.requests.add(co); }); c.urls.add(u); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java index e175b9d33ae8..cff1df72bef3 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java @@ -96,6 +96,11 @@ public class OpenAPINormalizer { // when set to true, normalize OpenAPI 3.1 spec to make it work with the generator final String NORMALIZE_31SPEC = "NORMALIZE_31SPEC"; + // when set to true, remove x-internal: true from models, operations + final String REMOVE_X_INTERNAL = "REMOVE_X_INTERNAL"; + final String X_INTERNAL = "x-internal"; + boolean removeXInternal; + // ============= end of rules ============= /** @@ -125,6 +130,7 @@ public OpenAPINormalizer(OpenAPI openAPI, Map inputRules) { ruleNames.add(ADD_UNSIGNED_TO_INTEGER_WITH_INVALID_MAX_VALUE); ruleNames.add(REFACTOR_ALLOF_WITH_PROPERTIES_ONLY); ruleNames.add(NORMALIZE_31SPEC); + ruleNames.add(REMOVE_X_INTERNAL); // rules that are default to true rules.put(SIMPLIFY_ONEOF_ANYOF, true); @@ -224,7 +230,6 @@ private void normalizePaths() { normalizeParameters(path.getParameters()); for (Operation operation : operations) { - normalizeOperation(operation); normalizeRequestBody(operation); normalizeParameters(operation.getParameters()); @@ -239,6 +244,8 @@ private void normalizePaths() { * @param operation Operation */ private void normalizeOperation(Operation operation) { + processRemoveXInternalFromOperation(operation); + processKeepOnlyFirstTagInOperation(operation); processSetTagsForAllOperations(operation); @@ -372,8 +379,15 @@ private void normalizeComponentsSchemas() { if (schema == null) { LOGGER.warn("{} not fount found in openapi/components/schemas.", schemaName); } else { - Schema result = normalizeSchema(schema, new HashSet<>()); - schemas.put(schemaName, result); + // remove x-internal if needed + if (schema.getExtensions() != null && getRule(REMOVE_X_INTERNAL)) { + if (Boolean.parseBoolean(String.valueOf(schema.getExtensions().get(X_INTERNAL)))) { + schema.getExtensions().remove(X_INTERNAL); + } + } + + // normalize the schemas + schemas.put(schemaName, normalizeSchema(schema, new HashSet<>())); } } } @@ -605,6 +619,26 @@ private void processUseAllOfRefAsParent(Schema schema) { } } + /** + * Keep only first tag in the operation if the operation has more than + * one tag. + * + * @param operation Operation + */ + private void processRemoveXInternalFromOperation(Operation operation) { + if (!getRule(REMOVE_X_INTERNAL)) { + return; + } + + if (operation.getExtensions() == null) { + return; + } + + if (Boolean.parseBoolean(String.valueOf(operation.getExtensions().get("x-internal")))) { + operation.getExtensions().remove(X_INTERNAL); + } + } + /** * Keep only first tag in the operation if the operation has more than * one tag. diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/OpenAPINormalizerTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/OpenAPINormalizerTest.java index 051e961f093a..e4175078c1ad 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/OpenAPINormalizerTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/OpenAPINormalizerTest.java @@ -414,4 +414,24 @@ public void testNormalize31Parameters() { assertNotNull(pathItem.getDelete().getParameters().get(0).getSchema().getType()); assertNotNull(pathItem.getDelete().getParameters().get(0).getSchema().getTypes()); } -} + + @Test + public void testRemoveXInternal() { + OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/enableKeepOnlyFirstTagInOperation_test.yaml"); + Schema s = openAPI.getComponents().getSchemas().get("Dummy"); + + assertEquals(openAPI.getPaths().get("/person/display/{personId}").getGet().getExtensions(), null); + assertEquals(openAPI.getPaths().get("/person/display/{personId}").getDelete().getExtensions().get("x-internal"), true); + assertEquals(s.getExtensions().get("x-internal"), true); + + Map options = new HashMap<>(); + options.put("REMOVE_X_INTERNAL", "true"); + OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options); + openAPINormalizer.normalize(); + + Schema s2 = openAPI.getComponents().getSchemas().get("Dummy"); + assertEquals(openAPI.getPaths().get("/person/display/{personId}").getGet().getExtensions(), null); + assertEquals(openAPI.getPaths().get("/person/display/{personId}").getDelete().getExtensions().get("x-internal"), null); + assertEquals(s2.getExtensions().get("x-internal"), null); + } +} \ No newline at end of file diff --git a/modules/openapi-generator/src/test/resources/3_0/enableKeepOnlyFirstTagInOperation_test.yaml b/modules/openapi-generator/src/test/resources/3_0/enableKeepOnlyFirstTagInOperation_test.yaml index 02c9017e6282..8b99e18d3ba1 100644 --- a/modules/openapi-generator/src/test/resources/3_0/enableKeepOnlyFirstTagInOperation_test.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/enableKeepOnlyFirstTagInOperation_test.yaml @@ -30,6 +30,7 @@ paths: delete: tags: - person + x-internal: true parameters: - name: personId in: path @@ -57,3 +58,10 @@ components: type: string firstName: type: string + Dummy: + x-internal: true + description: to test x-internal + type: object + properties: + test: + type: string \ No newline at end of file From 01ba6eb87101d79f5f6c775150430f30c393cea5 Mon Sep 17 00:00:00 2001 From: William Cheng Date: Mon, 29 Jan 2024 21:09:54 +0800 Subject: [PATCH 2/5] update --- .../codegen/DefaultGenerator.java | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java index b4ae0a7842f1..fc24b78888c7 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java @@ -1495,31 +1495,36 @@ private void processOperation(String resourcePath, String httpMethod, Operation final List globalSecurities = openAPI.getSecurity(); for (Tag tag : tags) { try { - CodegenOperation codegenOperation = config.fromOperation(resourcePath, httpMethod, operation, path.getServers()); - codegenOperation.tags = new ArrayList<>(tags); - config.addOperationToGroup(config.sanitizeTag(tag.getName()), resourcePath, operation, codegenOperation, operations); - - List securities = operation.getSecurity(); - if (securities != null && securities.isEmpty()) { - continue; - } + if (operation.getExtensions() != null && Boolean.TRUE.equals(operation.getExtensions().get("x-internal"))) { + // skip operation if x-internal sets to true + LOGGER.info("Operation ({} {} - {}) not generated since x-internal is set to true", + httpMethod, resourcePath, operation.getOperationId()); + } else { + CodegenOperation codegenOperation = config.fromOperation(resourcePath, httpMethod, operation, path.getServers()); + codegenOperation.tags = new ArrayList<>(tags); + config.addOperationToGroup(config.sanitizeTag(tag.getName()), resourcePath, operation, codegenOperation, operations); - Map authMethods = getAuthMethods(securities, securitySchemes); + List securities = operation.getSecurity(); + if (securities != null && securities.isEmpty()) { + continue; + } - if (authMethods != null && !authMethods.isEmpty()) { - List fullAuthMethods = config.fromSecurity(authMethods); - codegenOperation.authMethods = filterAuthMethods(fullAuthMethods, securities); - codegenOperation.hasAuthMethods = true; - } else { - authMethods = getAuthMethods(globalSecurities, securitySchemes); + Map authMethods = getAuthMethods(securities, securitySchemes); if (authMethods != null && !authMethods.isEmpty()) { List fullAuthMethods = config.fromSecurity(authMethods); - codegenOperation.authMethods = filterAuthMethods(fullAuthMethods, globalSecurities); + codegenOperation.authMethods = filterAuthMethods(fullAuthMethods, securities); codegenOperation.hasAuthMethods = true; + } else { + authMethods = getAuthMethods(globalSecurities, securitySchemes); + + if (authMethods != null && !authMethods.isEmpty()) { + List fullAuthMethods = config.fromSecurity(authMethods); + codegenOperation.authMethods = filterAuthMethods(fullAuthMethods, globalSecurities); + codegenOperation.hasAuthMethods = true; + } } } - } catch (Exception ex) { String msg = "Could not process operation:\n" // + " Tag: " + tag + "\n"// @@ -1530,7 +1535,6 @@ private void processOperation(String resourcePath, String httpMethod, Operation throw new RuntimeException(msg, ex); } } - } private static String generateParameterId(Parameter parameter) { From a2869c0dd844502dc79f8f9ccdec9ccf4719cdfc Mon Sep 17 00:00:00 2001 From: William Cheng Date: Mon, 29 Jan 2024 21:16:08 +0800 Subject: [PATCH 3/5] update doc --- docs/customization.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/customization.md b/docs/customization.md index ade155b1ba11..6b00ae907d52 100644 --- a/docs/customization.md +++ b/docs/customization.md @@ -571,3 +571,9 @@ Example: java -jar modules/openapi-generator-cli/target/openapi-generator-cli.jar generate -g java -i modules/openapi-generator/src/test/resources/3_0/allOf_extension_parent.yaml -o /tmp/java-okhttp/ --openapi-normalizer REFACTOR_ALLOF_WITH_PROPERTIES_ONLY=true ``` +- `REMOVE_X_INTERNAL`: When set to true, remove `x-internal` extension from operations and models. + +Example: +``` +java -jar modules/openapi-generator-cli/target/openapi-generator-cli.jar generate -g java -i modules/openapi-generator/src/test/resources/3_0/enableKeepOnlyFirstTagInOperation_test.yaml -o /tmp/java-okhttp/ --openapi-normalizer REMOVE_X_INTERNAL=true +``` From b802635784c23afe44bc94ffd2be3899b2394f83 Mon Sep 17 00:00:00 2001 From: William Cheng Date: Mon, 29 Jan 2024 21:18:00 +0800 Subject: [PATCH 4/5] better code format --- .../java/org/openapitools/codegen/OpenAPINormalizerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/OpenAPINormalizerTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/OpenAPINormalizerTest.java index e4175078c1ad..af29375dd55e 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/OpenAPINormalizerTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/OpenAPINormalizerTest.java @@ -385,7 +385,7 @@ public void testNormalize31Schema() { Schema pet = openAPI.getComponents().getSchemas().get("Pet"); // verify schema for property id - Schema petSchema = (Schema)pet.getProperties().get("id"); + Schema petSchema = (Schema) pet.getProperties().get("id"); // both type and types are defined assertNotNull(petSchema.getType()); assertNotNull(petSchema.getTypes()); From 1edda15077d02bfbfe0364a2311db8732842cabc Mon Sep 17 00:00:00 2001 From: William Cheng Date: Mon, 29 Jan 2024 21:26:41 +0800 Subject: [PATCH 5/5] update samples --- .../petstore/java/okhttp-gson/README.md | 1 - .../java/okhttp-gson/docs/ValuesApi.md | 60 --------- .../openapitools/client/api/ValuesApi.java | 117 ------------------ 3 files changed, 178 deletions(-) diff --git a/samples/client/petstore/java/okhttp-gson/README.md b/samples/client/petstore/java/okhttp-gson/README.md index 8cd4a7430f5f..73876bfde57c 100644 --- a/samples/client/petstore/java/okhttp-gson/README.md +++ b/samples/client/petstore/java/okhttp-gson/README.md @@ -159,7 +159,6 @@ Class | Method | HTTP request | Description *UserApi* | [**logoutUser**](docs/UserApi.md#logoutUser) | **GET** /user/logout | Logs out current logged in user session *UserApi* | [**updateUser**](docs/UserApi.md#updateUser) | **PUT** /user/{username} | Updated user *ValuesApi* | [**getSomeValues**](docs/ValuesApi.md#getSomeValues) | **GET** /values | Get some primitive variable values -*ValuesApi* | [**internalOnlyGet**](docs/ValuesApi.md#internalOnlyGet) | **GET** /internal/only | internal only ## Documentation for Models diff --git a/samples/client/petstore/java/okhttp-gson/docs/ValuesApi.md b/samples/client/petstore/java/okhttp-gson/docs/ValuesApi.md index a607e93df06e..4210bff03ec9 100644 --- a/samples/client/petstore/java/okhttp-gson/docs/ValuesApi.md +++ b/samples/client/petstore/java/okhttp-gson/docs/ValuesApi.md @@ -5,7 +5,6 @@ All URIs are relative to *http://petstore.swagger.io:80/v2* | Method | HTTP request | Description | |------------- | ------------- | -------------| | [**getSomeValues**](ValuesApi.md#getSomeValues) | **GET** /values | Get some primitive variable values | -| [**internalOnlyGet**](ValuesApi.md#internalOnlyGet) | **GET** /internal/only | internal only | @@ -67,62 +66,3 @@ No authorization required | **200** | successful operation | - | | **400** | Invalid Value | - | - -# **internalOnlyGet** -> Variable internalOnlyGet() - -internal only - - - -### Example -```java -// Import classes: -import org.openapitools.client.ApiClient; -import org.openapitools.client.ApiException; -import org.openapitools.client.Configuration; -import org.openapitools.client.models.*; -import org.openapitools.client.api.ValuesApi; - -public class Example { - public static void main(String[] args) { - ApiClient defaultClient = Configuration.getDefaultApiClient(); - defaultClient.setBasePath("http://petstore.swagger.io:80/v2"); - - ValuesApi apiInstance = new ValuesApi(defaultClient); - try { - Variable result = apiInstance.internalOnlyGet(); - System.out.println(result); - } catch (ApiException e) { - System.err.println("Exception when calling ValuesApi#internalOnlyGet"); - System.err.println("Status code: " + e.getCode()); - System.err.println("Reason: " + e.getResponseBody()); - System.err.println("Response headers: " + e.getResponseHeaders()); - e.printStackTrace(); - } - } -} -``` - -### Parameters -This endpoint does not need any parameter. - -### Return type - -[**Variable**](Variable.md) - -### Authorization - -No authorization required - -### HTTP request headers - - - **Content-Type**: Not defined - - **Accept**: application/json - -### HTTP response details -| Status code | Description | Response headers | -|-------------|-------------|------------------| -| **200** | successful operation | - | -| **400** | Invalid Value | - | - diff --git a/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/api/ValuesApi.java b/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/api/ValuesApi.java index 327ccfafe4c1..79fe043970d5 100644 --- a/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/api/ValuesApi.java +++ b/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/api/ValuesApi.java @@ -189,121 +189,4 @@ public okhttp3.Call getSomeValuesAsync(final ApiCallback _callback) th localVarApiClient.executeAsync(localVarCall, localVarReturnType, _callback); return localVarCall; } - /** - * Build call for internalOnlyGet - * @param _callback Callback for upload/download progress - * @return Call to execute - * @throws ApiException If fail to serialize the request body object - * @http.response.details - - - - -
Status Code Description Response Headers
200 successful operation -
400 Invalid Value -
- */ - public okhttp3.Call internalOnlyGetCall(final ApiCallback _callback) throws ApiException { - String basePath = null; - // Operation Servers - String[] localBasePaths = new String[] { }; - - // Determine Base Path to Use - if (localCustomBaseUrl != null){ - basePath = localCustomBaseUrl; - } else if ( localBasePaths.length > 0 ) { - basePath = localBasePaths[localHostIndex]; - } else { - basePath = null; - } - - Object localVarPostBody = null; - - // create path and map variables - String localVarPath = "/internal/only"; - - List localVarQueryParams = new ArrayList(); - List localVarCollectionQueryParams = new ArrayList(); - Map localVarHeaderParams = new HashMap(); - Map localVarCookieParams = new HashMap(); - Map localVarFormParams = new HashMap(); - - final String[] localVarAccepts = { - "application/json" - }; - final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); - if (localVarAccept != null) { - localVarHeaderParams.put("Accept", localVarAccept); - } - - final String[] localVarContentTypes = { - }; - final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); - if (localVarContentType != null) { - localVarHeaderParams.put("Content-Type", localVarContentType); - } - - String[] localVarAuthNames = new String[] { }; - return localVarApiClient.buildCall(basePath, localVarPath, "GET", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); - } - - @SuppressWarnings("rawtypes") - private okhttp3.Call internalOnlyGetValidateBeforeCall(final ApiCallback _callback) throws ApiException { - return internalOnlyGetCall(_callback); - - } - - /** - * internal only - * - * @return Variable - * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body - * @http.response.details - - - - -
Status Code Description Response Headers
200 successful operation -
400 Invalid Value -
- */ - public Variable internalOnlyGet() throws ApiException { - ApiResponse localVarResp = internalOnlyGetWithHttpInfo(); - return localVarResp.getData(); - } - - /** - * internal only - * - * @return ApiResponse<Variable> - * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body - * @http.response.details - - - - -
Status Code Description Response Headers
200 successful operation -
400 Invalid Value -
- */ - public ApiResponse internalOnlyGetWithHttpInfo() throws ApiException { - okhttp3.Call localVarCall = internalOnlyGetValidateBeforeCall(null); - Type localVarReturnType = new TypeToken(){}.getType(); - return localVarApiClient.execute(localVarCall, localVarReturnType); - } - - /** - * internal only (asynchronously) - * - * @param _callback The callback to be executed when the API call finishes - * @return The request call - * @throws ApiException If fail to process the API call, e.g. serializing the request body object - * @http.response.details - - - - -
Status Code Description Response Headers
200 successful operation -
400 Invalid Value -
- */ - public okhttp3.Call internalOnlyGetAsync(final ApiCallback _callback) throws ApiException { - - okhttp3.Call localVarCall = internalOnlyGetValidateBeforeCall(_callback); - Type localVarReturnType = new TypeToken(){}.getType(); - localVarApiClient.executeAsync(localVarCall, localVarReturnType, _callback); - return localVarCall; - } }