Repository: svix/svix-webhooks (language: rust)

Issue:
[JAVA] Patch requests serialize null values
## Bug Report

<!--
Thank you for reporting an issue.

Please fill in as much of the template below as you're able.
-->

### Version

<!--
List the versions of all `svix` crates you are using. The easiest way to get
this information is using `cargo tree`:

`cargo tree | grep svix`
-->
1.13.0
### Platform

<!---
Output of `uname -a` (UNIX), or version and 32 or 64-bit (Windows)
-->
Java svix library
### Description

<!--
Enter your issue details below this comment.

One way to structure the description:

<short summary of the bug>

I tried this code:

<code sample that causes the bug>

I expected to see this happen: <explanation>

Instead, this happened: <explanation>
-->

When calling Patch endpoints from java svix library, we want to partialy update an object, so in most of case we dont precises all field of the object but just the fields we want to update.

But the GSON in the svix java lib is configured to serialize null values to null instead of ommiting them for Patch endpoints



Discussion / hints:

Fix patch:
diff --git a/.github/workflows/other-lint.yml b/.github/workflows/other-lint.yml
index 9eda33e45..72ae19076 100644
--- a/.github/workflows/other-lint.yml
+++ b/.github/workflows/other-lint.yml
@@ -27,6 +27,7 @@ jobs:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
           VALIDATE_CHECKOV: false
           VALIDATE_PYTHON: false
+          VALIDATE_GOOGLE_JAVA_FORMAT: false # Only use checkstyle
           VALIDATE_JAVASCRIPT_ES: false
           VALIDATE_JAVASCRIPT_STANDARD: false
           VALIDATE_KOTLIN_ANDROID: false
diff --git a/java/lib/generated/openapi/src/main/java/com/svix/internal/JSON.java b/java/lib/generated/openapi/src/main/java/com/svix/internal/JSON.java
index b68c45fde..86919e2f4 100644
--- a/java/lib/generated/openapi/src/main/java/com/svix/internal/JSON.java
+++ b/java/lib/generated/openapi/src/main/java/com/svix/internal/JSON.java
@@ -1,13 +1,6 @@
 /*
- * Svix API
- * Welcome to the Svix API documentation!  Useful links: [Homepage](https://www.svix.com) | [Support email](mailto:support+docs@svix.com) | [Blog](https://www.svix.com/blog/) | [Slack Community](https://www.svix.com/slack/)  # Introduction  This is the reference documentation and schemas for the [Svix webhook service](https://www.svix.com) API. For tutorials and other documentation please refer to [the documentation](https://docs.svix.com).  ## Main concepts  In Svix you have four important entities you will be interacting with:  - `messages`: these are the webhooks being sent. They can have contents and a few other properties. - `application`: this is where `messages` are sent to. Usually you want to create one application for each user on your platform. - `endpoint`: endpoints are the URLs messages will be sent to. Each application can have multiple `endpoints` and each message sent to that application will be sent to all of them (unless they are not subscribed to the sent event type). - `event-type`: event types are identifiers denoting the type of the message being sent. Event types are primarily used to decide which events are sent to which endpoint.   ## Authentication  Get your authentication token (`AUTH_TOKEN`) from the [Svix dashboard](https://dashboard.svix.com) and use it as part of the `Authorization` header as such: `Authorization: Bearer ${AUTH_TOKEN}`.  <SecurityDefinitions />   ## Code samples  The code samples assume you already have the respective libraries installed and you know how to use them. For the latest information on how to do that, please refer to [the documentation](https://docs.svix.com/).   ## Idempotency  Svix supports [idempotency](https://en.wikipedia.org/wiki/Idempotence) for safely retrying requests without accidentally performing the same operation twice. This is useful when an API call is disrupted in transit and you do not receive a response.  To perform an idempotent request, pass the idempotency key in the `Idempotency-Key` header to the request. The idempotency key should be a unique value generated by the client. You can create the key in however way you like, though we suggest using UUID v4, or any other string with enough entropy to avoid collisions.  Svix's idempotency works by saving the resulting status code and body of the first request made for any given idempotency key for any successful request. Subsequent requests with the same key return the same result.  Please note that idempotency is only supported for `POST` requests.   ## Cross-Origin Resource Sharing  This API features Cross-Origin Resource Sharing (CORS) implemented in compliance with [W3C spec](https://www.w3.org/TR/cors/). And that allows cross-domain communication from the browser. All responses have a wildcard same-origin which makes them completely public and accessible to everyone, including any code on any site.
- *
- * The version of the OpenAPI document: 1.4
- *
- *
- * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
- * https://openapi-generator.tech
- * Do not edit the class manually.
+ * NOTE: This class is a manually modified version of a class auto generated by OpenAPI Generator,
+ * to add specific JSON serialization and deserialization depending on the API's needs.
  *
  * @generated
  */
@@ -54,41 +47,40 @@ public static GsonBuilder createGson() {
         return builder;
     }
 
-    private static String getDiscriminatorValue(final JsonElement readElement, final String discriminatorField) {
+    private static String getDiscriminatorValue(final JsonElement readElement,
+                                                final String discriminatorField) {
         JsonElement element = readElement.getAsJsonObject().get(discriminatorField);
         if (null == element) {
-            throw new IllegalArgumentException("missing discriminator field: <" + discriminatorField + ">");
+            throw new IllegalArgumentException(
+                    "missing discriminator field: <" + discriminatorField + ">");
         }
         return element.getAsString();
     }
 
     /**
-     * Returns the Java class that implements the OpenAPI schema for the specified
-     * discriminator value.
+     * Returns the Java class that implements the OpenAPI schema for the specified discriminator
+     * value.
      *
-     * @param classByDiscriminatorValue The map of discriminator values to Java
-     *                                  classes.
-     * @param discriminatorValue        The value of the OpenAPI discriminator in
-     *                                  the input data.
+     * @param classByDiscriminatorValue The map of discriminator values to Java classes.
+     * @param discriminatorValue        The value of the OpenAPI discriminator in the input data.
      * @return The Java class that implements the OpenAPI schema
      */
-    private static Class getClassByDiscriminator(final Map classByDiscriminatorValue, final String discriminatorValue) {
+    private static Class getClassByDiscriminator(final Map classByDiscriminatorValue,
+                                                 final String discriminatorValue) {
         Class clazz = (Class) classByDiscriminatorValue.get(discriminatorValue);
         if (null == clazz) {
-            throw new IllegalArgumentException("cannot determine model class of name: <" + discriminatorValue + ">");
+            throw new IllegalArgumentException(
+                    "cannot determine model class of name: <" + discriminatorValue + ">");
         }
         return clazz;
     }
 
     public JSON() {
-        gson = createGson()
-                .registerTypeAdapter(Date.class, dateTypeAdapter)
+        gson = createGson().registerTypeAdapter(Date.class, dateTypeAdapter)
                 .registerTypeAdapter(java.sql.Date.class, sqlDateTypeAdapter)
                 .registerTypeAdapter(OffsetDateTime.class, offsetDateTimeTypeAdapter)
                 .registerTypeAdapter(LocalDate.class, localDateTypeAdapter)
-                .registerTypeAdapter(byte[].class, byteArrayAdapter)
-                .serializeNulls()
-                .create();
+                .registerTypeAdapter(byte[].class, byteArrayAdapter).create();
     }
 
     /**
@@ -116,6 +108,11 @@ public JSON setLenientOnJson(final boolean lenientOnJson) {
         return this;
     }
 
+    public JSON serializeNulls() {
+        gson = gson.newBuilder().serializeNulls().create();
+        return this;
+    }
+
     /**
      * Serialize the given Java object into JSON string.
      *
@@ -281,9 +278,8 @@ public JSON setLocalDateFormat(final DateTimeFormatter dateFormat) {
     }
 
     /**
-     * Gson TypeAdapter for java.sql.Date type
-     * If the dateFormat is null, a simple "yyyy-MM-dd" format will be used
-     * (more efficient than SimpleDateFormat).
+     * Gson TypeAdapter for java.sql.Date type If the dateFormat is null, a simple "yyyy-MM-dd"
+     * format will be used (more efficient than SimpleDateFormat).
      */
     public static class SqlDateTypeAdapter extends TypeAdapter<java.sql.Date> {
 
@@ -327,7 +323,8 @@ public java.sql.Date read(final JsonReader in) throws IOException {
                         if (dateFormat != null) {
                             return new java.sql.Date(dateFormat.parse(date).getTime());
                         }
-                        return new java.sql.Date(ISO8601Utils.parse(date, new ParsePosition(0)).getTime());
+                        return new java.sql.Date(
+                                ISO8601Utils.parse(date, new ParsePosition(0)).getTime());
                     } catch (ParseException e) {
                         throw new JsonParseException(e);
                     }
@@ -336,8 +333,8 @@ public java.sql.Date read(final JsonReader in) throws IOException {
     }
 
     /**
-     * Gson TypeAdapter for java.util.Date type
-     * If the dateFormat is null, ISO8601Utils will be used.
+     * Gson TypeAdapter for java.util.Date type If the dateFormat is null, ISO8601Utils will be
+     * used.
      */
     public static class DateTypeAdapter extends TypeAdapter<Date> {
 
diff --git a/java/templates/libraries/okhttp-gson/ApiClient.mustache b/java/templates/libraries/okhttp-gson/ApiClient.mustache
index a3841b696..aa889a4e3 100644
--- a/java/templates/libraries/okhttp-gson/ApiClient.mustache
+++ b/java/templates/libraries/okhttp-gson/ApiClient.mustache
@@ -88,6 +88,7 @@ public class ApiClient {
 
     private OkHttpClient httpClient;
     private JSON json;
+    private JSON patchJson;
 
     private HttpLoggingInterceptor loggingInterceptor;
 
@@ -213,6 +214,8 @@ public class ApiClient {
         verifyingSsl = true;
 
         json = new JSON();
+        json = json.serializeNulls();
+        patchJson = new JSON();
 
         // Set default User-Agent.
         setUserAgent("{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}OpenAPI-Generator/{{{artifactVersion}}}/java{{/httpUserAgent}}");
@@ -1006,7 +1009,7 @@ public class ApiClient {
      * @return The serialized request body
      * @throws ApiException If fail to serialize the given object
      */
-    public RequestBody serialize(Object obj, String contentType) throws ApiException {
+    public RequestBody serialize(JSON jsonSerializer, Object obj, String contentType) throws ApiException {
         if (obj instanceof byte[]) {
             // Binary (byte array) body parameter support.
             return RequestBody.create((byte[]) obj, MediaType.parse(contentType));
@@ -1016,7 +1019,7 @@ public class ApiClient {
         } else if (isJsonMime(contentType)) {
             String content;
             if (obj != null) {
-                content = json.serialize(obj);
+                content = jsonSerializer.serialize(obj);
             } else {
                 content = null;
             }
@@ -1294,8 +1297,10 @@ public class ApiClient {
                 // use an empty request body (for POST, PUT and PATCH)
                 reqBody = RequestBody.create("", MediaType.parse(contentType));
             }
+        } else if ("PATCH".equals(method)) {
+            reqBody = serialize(patchJson, body, contentType); 
         } else {
-            reqBody = serialize(body, contentType);
+            reqBody = serialize(json, body, contentType);
         }
 
         // Associate callback with request (if not null) so interceptor can


Test patch: