From 8beb47f8523b727957cd2f281bbb8e9eff935ad2 Mon Sep 17 00:00:00 2001 From: kridai Date: Tue, 4 Nov 2025 16:23:56 +0530 Subject: [PATCH] add patch operation support --- examples/spec/twilio_flex_v1.yaml | 4 +- .../com/twilio/oai/common/EnumConstants.java | 5 +- .../twilio/oai/java/JavaTemplateUpdater.java | 29 ++++++++-- .../oai/java/constants/MustacheConstants.java | 12 +++-- .../oai/template/JavaApiActionTemplate.java | 2 + .../twilio-java/common/imports.mustache | 3 +- .../resources/twilio-java/patcher.mustache | 28 ++++++++++ .../patcher/operationMethod.mustache | 54 +++++++++++++++++++ .../twilio-java/patcher/setters.mustache | 20 +++++++ 9 files changed, 142 insertions(+), 15 deletions(-) create mode 100644 src/main/resources/twilio-java/patcher.mustache create mode 100644 src/main/resources/twilio-java/patcher/operationMethod.mustache create mode 100644 src/main/resources/twilio-java/patcher/setters.mustache diff --git a/examples/spec/twilio_flex_v1.yaml b/examples/spec/twilio_flex_v1.yaml index 0d182b27a..920c3fa6e 100644 --- a/examples/spec/twilio_flex_v1.yaml +++ b/examples/spec/twilio_flex_v1.yaml @@ -251,8 +251,8 @@ paths: description: OK security: - accountSid_authToken: [ ] - post: - operationId: UpdateCredentialAws + patch: + operationId: PatchCredentialAws parameters: - in: path name: Sid diff --git a/src/main/java/com/twilio/oai/common/EnumConstants.java b/src/main/java/com/twilio/oai/common/EnumConstants.java index 06b8b6ccd..799bd9e9f 100644 --- a/src/main/java/com/twilio/oai/common/EnumConstants.java +++ b/src/main/java/com/twilio/oai/common/EnumConstants.java @@ -151,7 +151,7 @@ public enum OpenApiEnumType { FORM_PARAM_REF, FORM_PARAM_LIST_REF, } - + public enum ModelType { SINGLE, LIST @@ -164,7 +164,8 @@ public enum SupportedOperation { X_LIST("x-list-operation"), X_UPDATE("x-update-operation"), X_FETCH("x-fetch-operation"), - X_DELETE("x-delete-operation"); + X_DELETE("x-delete-operation"), + X_PATCH("x-patch-operation"); private final String value; } diff --git a/src/main/java/com/twilio/oai/java/JavaTemplateUpdater.java b/src/main/java/com/twilio/oai/java/JavaTemplateUpdater.java index 97d618ca5..755dbee68 100644 --- a/src/main/java/com/twilio/oai/java/JavaTemplateUpdater.java +++ b/src/main/java/com/twilio/oai/java/JavaTemplateUpdater.java @@ -2,6 +2,7 @@ import com.twilio.oai.TwilioJavaGeneratorModern; import com.twilio.oai.common.EnumConstants; +import com.twilio.oai.common.EnumConstants.SupportedOperation; import com.twilio.oai.java.constants.MustacheConstants; import org.openapitools.codegen.CodegenOperation; @@ -12,9 +13,9 @@ import static com.twilio.oai.java.constants.MustacheConstants.ActionType; /* -The JavaTemplateFile class is responsible for managing template mappings for Java code generation. -It defines mappings between operation IDs (starting strings) -and corresponding template files (mustache files) along with their generated file extensions. +The JavaTemplateFile class is responsible for managing template mappings for Java code generation. +It defines mappings between operation IDs (starting strings) +and corresponding template files (mustache files) along with their generated file extensions. Example: Key: Represents the starting string of the operationId (e.g., "create", "fetch"). Value: Represents a mapping between the mustache template file and the generated file extension using AbstractMap.SimpleEntry. @@ -34,7 +35,8 @@ public JavaTemplateUpdater() { "fetch", new AbstractMap.SimpleEntry<>("fetcher.mustache", "Fetcher.java"), "delete", new AbstractMap.SimpleEntry<>("deleter.mustache", "Deleter.java"), "list", new AbstractMap.SimpleEntry<>("reader.mustache", "Reader.java"), - "update", new AbstractMap.SimpleEntry<>("updater.mustache", "Updater.java") + "update", new AbstractMap.SimpleEntry<>("updater.mustache", "Updater.java"), + "patch", new AbstractMap.SimpleEntry<>("patcher.mustache", "Patcher.java") ); apiTemplate = Map.of( API_TEMPLATE, new AbstractMap.SimpleEntry<>("api.mustache", ".java") @@ -61,6 +63,8 @@ public void addApiTemplate(TwilioJavaGeneratorModern twilioJavaGenerator, java.u Delete.add(twilioJavaGenerator, operation, apiOperationTemplate); } else if (Fetch.isFetch(operation)) { Fetch.add(twilioJavaGenerator, operation, apiOperationTemplate); + } else if (Patch.isPatch(operation)) { + Patch.add(twilioJavaGenerator, operation, apiOperationTemplate); } else { throw new RuntimeException("Unsupported operation type for operationId: " + operationId); } @@ -149,4 +153,19 @@ public static void add(TwilioJavaGeneratorModern twilioJavaGenerator, CodegenOpe public static boolean isFetch(CodegenOperation codegenOperation) { return codegenOperation.operationId.toLowerCase().startsWith("fetch"); } -} \ No newline at end of file +} + +class Patch { + public static void add(TwilioJavaGeneratorModern twilioJavaGenerator, CodegenOperation codegenOperation, Map apiOperationTemplate) { + codegenOperation.vendorExtensions.put(SupportedOperation.X_PATCH.getValue(), true); + String key = (String) apiOperationTemplate.get("patch").getKey(); + String value = (String) apiOperationTemplate.get("patch").getValue(); + twilioJavaGenerator.apiTemplateFiles().put(key, value); + + codegenOperation.vendorExtensions.put(MustacheConstants.ACTION_TYPE, ActionType.PATCHER.getValue()); + codegenOperation.vendorExtensions.put(MustacheConstants.ACTION_METHOD, ActionMethod.PATCH.getValue()); + } + public static boolean isPatch(CodegenOperation codegenOperation) { + return codegenOperation.operationId.toLowerCase().startsWith("patch"); + } +} diff --git a/src/main/java/com/twilio/oai/java/constants/MustacheConstants.java b/src/main/java/com/twilio/oai/java/constants/MustacheConstants.java index 6dce31699..debe7dff0 100644 --- a/src/main/java/com/twilio/oai/java/constants/MustacheConstants.java +++ b/src/main/java/com/twilio/oai/java/constants/MustacheConstants.java @@ -18,9 +18,9 @@ public class MustacheConstants { public static final String X_IS_LIST_OP = "x-is-list-op"; public static final String ACTION_TYPE = "x-common-action-type"; public static final String ACTION_METHOD = "x-common-action-method"; - + public static final Map serializaationMapping = Map.of( - "application/x-www-form-urlencoded", "if ($paramName != null) { request.addPostParam($stringCapParamName, $paramName.toString())}", + "application/x-www-form-urlencoded", "if ($paramName != null) { request.addPostParam($stringCapParamName, $paramName.toString())}", "application/json", "Json", "multipart/form-data", "MultipartFormData" ); @@ -34,7 +34,8 @@ public enum ActionType { READER("Reader"), UPDATER("Updater"), DELETER("Deleter"), - FETCHER("Fetcher"); + FETCHER("Fetcher"), + PATCHER("Patcher"); private final String value; } @@ -47,9 +48,10 @@ public enum ActionMethod { READ("read"), UPDATE("update"), DELETE("delete"), - FETCH("fetch"); + FETCH("fetch"), + PATCH("patch"); private final String value; } - + } diff --git a/src/main/java/com/twilio/oai/template/JavaApiActionTemplate.java b/src/main/java/com/twilio/oai/template/JavaApiActionTemplate.java index dfaab93ba..24d2b59d2 100644 --- a/src/main/java/com/twilio/oai/template/JavaApiActionTemplate.java +++ b/src/main/java/com/twilio/oai/template/JavaApiActionTemplate.java @@ -23,6 +23,8 @@ public Map> mapping() { Arrays.asList("reader.mustache", "Reader.java"), "update", Arrays.asList("updater.mustache", "Updater.java"), + "patch", + Arrays.asList("updater.mustache", "Updater.java"), API_TEMPLATE, Arrays.asList("api.mustache", ".java"), NESTED_MODELS, diff --git a/src/main/resources/twilio-java/common/imports.mustache b/src/main/resources/twilio-java/common/imports.mustache index 2dce9d03f..56be17f19 100644 --- a/src/main/resources/twilio-java/common/imports.mustache +++ b/src/main/resources/twilio-java/common/imports.mustache @@ -11,6 +11,7 @@ import com.twilio.base.Deleter; import com.twilio.base.Fetcher; import com.twilio.base.Reader; import com.twilio.base.Updater; +import com.twilio.base.Patcher; import com.twilio.constant.EnumConstants; import com.twilio.constant.EnumConstants.ParameterType; import com.twilio.converter.Promoter; @@ -57,4 +58,4 @@ import java.io.IOException; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; -import com.fasterxml.jackson.core.JsonProcessingException; \ No newline at end of file +import com.fasterxml.jackson.core.JsonProcessingException; diff --git a/src/main/resources/twilio-java/patcher.mustache b/src/main/resources/twilio-java/patcher.mustache new file mode 100644 index 000000000..822f6179a --- /dev/null +++ b/src/main/resources/twilio-java/patcher.mustache @@ -0,0 +1,28 @@ +{{>licenseInfo}} +{{#resources}} +package com.twilio.rest.{{domainPackage}}.{{apiVersion}}{{namespaceSubPart}}; +{{>common/imports}} + +{{#operations}} +{{#vendorExtensions.x-patch-operation}} + public class {{resourceName}}Patcher extends Patcher<{{resourceName}}> { + {{>common/instanceVariables}} + {{>common/constructors}} + {{>patcher/setters}} + {{>patcher/operationMethod}} + {{#queryParams.0}} + {{>common/addQueryParams}} + {{/queryParams.0}} + {{#formParams.0}} + {{>common/addPostParams}} + {{/formParams.0}} + {{#headerParams.0}} + {{>common/addHeaderParams}} + {{/headerParams.0}} + {{#bodyParams.0}} + {{>common/addPostParamsJson}} + {{/bodyParams.0}} + } +{{/vendorExtensions.x-patch-operation}} +{{/operations}} +{{/resources}} diff --git a/src/main/resources/twilio-java/patcher/operationMethod.mustache b/src/main/resources/twilio-java/patcher/operationMethod.mustache new file mode 100644 index 000000000..4817dded3 --- /dev/null +++ b/src/main/resources/twilio-java/patcher/operationMethod.mustache @@ -0,0 +1,54 @@ +{{! +resourceName: Api Name as identified by Directory Structure service +x-common-action-method: used to define operation method and can have values: create, read, update, delete, fetch, patch + Example: https://github.com/twilio/twilio-java/blob/9c2ba4dbc185c5576e67fbeb82ec6f4899093e79/src/main/java/com/twilio/rest/api/v2010/account/MessageCreator.java#L309 + +httpMethod: http method associated in the current operation. +domainName: example api, video, chat, etc. These can be found in Domains.java in twilio-java +vendorExtensions.x-content-type: content type of the request, example: application/json, application/x-www-form-urlencoded +}} + @Override + public {{resourceName}} {{vendorExtensions.x-common-action-method}}(final TwilioRestClient client) { + {{>common/generateUri}} + + Request request = new Request( + HttpMethod.{{httpMethod}}, + Domains.{{#lambda.uppercase}}{{domainName}}{{/lambda.uppercase}}.toString(), + path + ); + {{#vendorExtensions.x-request-content-type}} + request.setContentType(EnumConstants.ContentType.{{vendorExtensions.x-request-content-type}}); + {{/vendorExtensions.x-request-content-type}} + {{#vendorExtensions.x-no-auth}} + request.setAuth(NoAuthStrategy.getInstance()); + {{/vendorExtensions.x-no-auth}} + {{#queryParams.0}} + addQueryParams(request); + {{/queryParams.0}} + {{#headerParams.0}} + addHeaderParams(request); + {{/headerParams.0}} + {{#formParams.0}} + addPostParams(request); + {{/formParams.0}} + {{#bodyParams.0}} + addPostParams(request, client); + {{/bodyParams.0}} + + Response response = client.request(request); + + if (response == null) { + throw new ApiConnectionException("{{resourceName}} patch failed: Unable to connect to server"); + } else if (!TwilioRestClient.SUCCESS.test(response.getStatusCode())) { + RestException restException = RestException.fromJson( + response.getStream(), + client.getObjectMapper() + ); + if (restException == null) { + throw new ApiException("Server Error, no content", response.getStatusCode()); + } + throw new ApiException(restException); + } + + return {{resourceName}}.fromJson(response.getStream(), client.getObjectMapper()); + } diff --git a/src/main/resources/twilio-java/patcher/setters.mustache b/src/main/resources/twilio-java/patcher/setters.mustache new file mode 100644 index 000000000..3fd5bd68a --- /dev/null +++ b/src/main/resources/twilio-java/patcher/setters.mustache @@ -0,0 +1,20 @@ +{{#vendorExtensions.x-setter-methods}} + +public {{resourceName}}Patcher set{{#lambda.titlecase}}{{paramName}}{{/lambda.titlecase}}(final {{{dataType}}} {{paramName}}){ + this.{{paramName}} = {{paramName}}; + return this; +} + +{{! Optional setter-- Ideally this shouldn't exists, keeping it for backward compatibility }} +{{#isArray}} +public {{resourceName}}Patcher set{{#lambda.titlecase}}{{paramName}}{{/lambda.titlecase}}(final {{{baseType}}} {{paramName}}){ + return set{{#lambda.titlecase}}{{paramName}}{{/lambda.titlecase}}(Promoter.listOfOne({{paramName}})); +} +{{/isArray}} +{{! Optional setter, vendorExtensions.x-promotion stores promotion method}} +{{#vendorExtensions.x-promotion}} +public {{resourceName}}Patcher set{{#lambda.titlecase}}{{paramName}}{{/lambda.titlecase}}(final String {{paramName}}){ + return set{{#lambda.titlecase}}{{paramName}}{{/lambda.titlecase}}({{vendorExtensions.x-promotion}}); +} +{{/vendorExtensions.x-promotion}} +{{/vendorExtensions.x-setter-methods}}