Skip to content

Commit

Permalink
java/kotlin: Send empty struct instead of empty string (#1786)
Browse files Browse the repository at this point in the history
Fixes regression introduced in
#1783
  • Loading branch information
svix-jplatte authored Mar 4, 2025
2 parents 6cc1505 + 4cf0d1f commit eb21ef9
Show file tree
Hide file tree
Showing 15 changed files with 190 additions and 48 deletions.
55 changes: 46 additions & 9 deletions java/lib/src/main/java/com/svix/api/Message.java
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
// this file is @generated
package com.svix.api;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.svix.SvixHttpClient;
import com.svix.Utils;
import com.svix.exceptions.ApiException;
import com.svix.models.*;
import com.svix.models.ExpungeAllContentsOut;
import com.svix.models.ListResponseMessageOut;
import com.svix.models.MessageIn;
import com.svix.models.MessageOut;

import lombok.*;

import okhttp3.Headers;
import okhttp3.HttpUrl;

import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Message {
private final SvixHttpClient client;
Expand Down Expand Up @@ -135,22 +141,23 @@ public MessageOut create(
if (options.idempotencyKey != null) {
headers.put("idempotency-key", options.idempotencyKey);
}
if (messageIn.getTransformationsParams() != null) {
if (messageIn.getTransformationsParams().get("rawPayload") == null) {
MessageInInternal msgInInternal = new MessageInInternal(messageIn);
if (msgInInternal.transformationsParams != null) {
if (msgInInternal.transformationsParams.get("rawPayload") == null) {
// transformationsParams may be immutable
HashMap<String, Object> trParams =
new HashMap<>(messageIn.getTransformationsParams());
trParams.put("rawPayload", messageIn.getPayload());
messageIn.setTransformationsParams(trParams);
new HashMap<>(msgInInternal.transformationsParams);
trParams.put("rawPayload", msgInInternal.payload);
msgInInternal.transformationsParams = trParams;
}
} else {
HashMap<String, Object> trParam = new HashMap<>();
trParam.put("rawPayload", messageIn.getPayload());
messageIn.setTransformationsParams(trParam);
trParam.put("rawPayload", msgInInternal.payload);
msgInInternal.transformationsParams = trParam;
}
messageIn.setPayload("");
msgInInternal.payload = new HashMap<>();
return this.client.executeRequest(
"POST", url.build(), Headers.of(headers), messageIn, MessageOut.class);
"POST", url.build(), Headers.of(headers), msgInInternal, MessageOut.class);
}

/**
Expand Down Expand Up @@ -251,4 +258,34 @@ public static MessageIn messageInRaw(final String payload, final String contentT
msg.setTransformationsParams(trParam);
return msg;
}

@ToString
@EqualsAndHashCode
@JsonInclude(JsonInclude.Include.NON_NULL)
@NoArgsConstructor
// we use this because we need payload to be an object while the public `MessageIn.payload` is a
// string
private class MessageInInternal {
@JsonProperty private ApplicationIn application;
@JsonProperty private Set<String> channels;
@JsonProperty private String eventId;
@JsonProperty private String eventType;
@JsonProperty private Object payload;
@JsonProperty private Long payloadRetentionHours;
@JsonProperty private Long payloadRetentionPeriod;
@JsonProperty private Set<String> tags;
@JsonProperty private Map<String, Object> transformationsParams;

private MessageInInternal(MessageIn messageIn) {
this.application = messageIn.getApplication();
this.channels = messageIn.getChannels();
this.eventId = messageIn.getEventId();
this.eventType = messageIn.getEventType();
this.payload = messageIn.getPayload();
this.payloadRetentionHours = messageIn.getPayloadRetentionHours();
this.payloadRetentionPeriod = messageIn.getPayloadRetentionPeriod();
this.tags = messageIn.getTags();
this.transformationsParams = messageIn.getTransformationsParams();
}
}
}
2 changes: 1 addition & 1 deletion java/lib/src/test/com/svix/test/WiremockTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ public void msgInRaw() throws Exception {
svx.getMessage().create("app1", msg);

String expectedBody =
"{\"eventType\":\"event.ended\",\"payload\":\"\",\"transformationsParams\":{\"rawPayload\":\"<xml>{no"
"{\"eventType\":\"event.ended\",\"payload\":{},\"transformationsParams\":{\"rawPayload\":\"<xml>{no"
+ " json here}\"}}";
wireMockRule.verify(
1,
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"payload":"","transformationsParams":{"rawPayload":"{\"key1\":\"val\",\"key2\":\"val\",\"key\":\"val\"}"}}
{"payload":{},"transformationsParams":{"rawPayload":"{\"key1\":\"val\",\"key2\":\"val\",\"key\":\"val\"}"}}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"eventType":"event.type","payload":"","transformationsParams":{"rawPayload":"{\"key\":\"val\",\"key1\":[\"list\"]}"}}
{"eventType":"event.type","payload":{},"transformationsParams":{"rawPayload":"{\"key\":\"val\",\"key1\":[\"list\"]}"}}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"eventType":"event.type","payload":"","transformationsParams":{"rawPayload":"{\"key\":\"val\",\"key1\":[\"list\"]}","headers":{"header-key":"header-val"}}}
{"eventType":"event.type","payload":{},"transformationsParams":{"rawPayload":"{\"key\":\"val\",\"key1\":[\"list\"]}","headers":{"header-key":"header-val"}}}
31 changes: 31 additions & 0 deletions java/templates/api_extra/message.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,34 @@ public static MessageIn messageInRaw(final String payload, final String contentT
msg.setTransformationsParams(trParam);
return msg;
}


@ToString
@EqualsAndHashCode
@JsonInclude(JsonInclude.Include.NON_NULL)
@NoArgsConstructor
// we use this because we need payload to be an object while the public `MessageIn.payload` is a
// string
private class MessageInInternal {
@JsonProperty private ApplicationIn application;
@JsonProperty private Set<String> channels;
@JsonProperty private String eventId;
@JsonProperty private String eventType;
@JsonProperty private Object payload;
@JsonProperty private Long payloadRetentionHours;
@JsonProperty private Long payloadRetentionPeriod;
@JsonProperty private Set<String> tags;
@JsonProperty private Map<String, Object> transformationsParams;

private MessageInInternal(MessageIn messageIn) {
this.application = messageIn.getApplication();
this.channels = messageIn.getChannels();
this.eventId = messageIn.getEventId();
this.eventType = messageIn.getEventType();
this.payload = messageIn.getPayload();
this.payloadRetentionHours = messageIn.getPayloadRetentionHours();
this.payloadRetentionPeriod = messageIn.getPayloadRetentionPeriod();
this.tags = messageIn.getTags();
this.transformationsParams = messageIn.getTransformationsParams();
}
}
18 changes: 10 additions & 8 deletions java/templates/api_extra/message_create_body.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
if (messageIn.getTransformationsParams() != null) {
if (messageIn.getTransformationsParams().get("rawPayload") == null) {
MessageInInternal msgInInternal = new MessageInInternal(messageIn);
if (msgInInternal.transformationsParams != null) {
if (msgInInternal.transformationsParams.get("rawPayload") == null) {
// transformationsParams may be immutable
HashMap<String, Object> trParams = new HashMap<>(messageIn.getTransformationsParams());
trParams.put("rawPayload",messageIn.getPayload());
messageIn.setTransformationsParams(trParams);
HashMap<String, Object> trParams =
new HashMap<>(msgInInternal.transformationsParams);
trParams.put("rawPayload", msgInInternal.payload);
msgInInternal.transformationsParams = trParams;
}
} else {
HashMap<String, Object> trParam = new HashMap<>();
trParam.put("rawPayload", messageIn.getPayload());
messageIn.setTransformationsParams(trParam);
trParam.put("rawPayload", msgInInternal.payload);
msgInInternal.transformationsParams = trParam;
}
messageIn.setPayload("");
msgInInternal.payload = new HashMap<>();
24 changes: 18 additions & 6 deletions java/templates/api_resource.java.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,25 @@
// this file is @generated
package com.svix.api;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.svix.SvixHttpClient;
import com.svix.Utils;
import com.svix.exceptions.ApiException;

import okhttp3.Headers;
import okhttp3.HttpUrl;

import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import okhttp3.Headers;
import okhttp3.HttpUrl;
{% if resource.name == "message"%}
import lombok.*;
import com.svix.models.*;
{% endif -%}
{% for c in referenced_components -%}
import com.svix.models.{{ c | to_upper_camel_case }};
{% endfor -%}
Expand Down Expand Up @@ -140,10 +148,14 @@ public class {{ resource_type_name }} {
null,
{% endif -%}
{# request body -#}
{% if op.request_body_schema_name is defined -%}
{{ req_body_field_name }},
{% if op.id == "v1.message.create" -%}
msgInInternal,
{% else -%}
null,
{% if op.request_body_schema_name is defined -%}
{{ req_body_field_name }},
{% else -%}
null,
{% endif -%}
{% endif -%}
{# response body class -#}
{% if op.response_body_schema_name is defined -%}
Expand Down
43 changes: 35 additions & 8 deletions kotlin/lib/src/main/kotlin/Message.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.svix.kotlin.models.ListResponseMessageOut
import com.svix.kotlin.models.MessageIn
import com.svix.kotlin.models.MessageOut
import kotlinx.datetime.Instant
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
Expand Down Expand Up @@ -99,23 +100,35 @@ class Message(private val client: SvixHttpClient) {
options.withContent?.let { url.addQueryParameter("with_content", serializeQueryParam(it)) }
val headers = Headers.Builder()
options.idempotencyKey?.let { headers.add("idempotency-key", it) }
if (messageIn.transformationsParams != null) {
var msgInInternal =
MessageInInternal(
messageIn.application,
messageIn.channels,
messageIn.eventId,
messageIn.eventType,
mapOf(),
messageIn.payloadRetentionHours,
messageIn.payloadRetentionPeriod,
messageIn.tags,
messageIn.transformationsParams,
)
if (msgInInternal.transformationsParams != null) {
// only set rawPayload if not already set
if (messageIn.transformationsParams!!["rawPayload"] == null) {
var trParams = (messageIn.transformationsParams as Map<String, Any>).toMutableMap()
if (msgInInternal.transformationsParams!!["rawPayload"] == null) {
var trParams =
(msgInInternal.transformationsParams as Map<String, Any>).toMutableMap()
trParams["rawPayload"] = messageIn.payload
messageIn.transformationsParams = trParams.toMap()
msgInInternal.transformationsParams = trParams.toMap()
}
} else {
val trParams = mapOf("rawPayload" to messageIn.payload)
messageIn.transformationsParams = trParams
msgInInternal.transformationsParams = trParams
}
messageIn.payload = ""
return client.executeRequest<MessageIn, MessageOut>(
return client.executeRequest<MessageInInternal, MessageOut>(
"POST",
url.build(),
headers = headers.build(),
reqBody = messageIn,
reqBody = msgInInternal,
)
}

Expand Down Expand Up @@ -206,3 +219,17 @@ fun messageInRaw(
transformationsParams = JsonObject(transformationsParams),
)
}

@Serializable
private data class MessageInInternal(
val application: ApplicationIn? = null,
val channels: Set<String>? = null,
val eventId: String? = null,
val eventType: String,
@Serializable(with = StringAnyMapSerializer::class) var payload: Map<String, Any>,
val payloadRetentionHours: Long? = null,
val payloadRetentionPeriod: Long? = null,
val tags: Set<String>? = null,
@Serializable(with = StringAnyMapSerializer::class)
var transformationsParams: Map<String, Any>? = null,
)
2 changes: 1 addition & 1 deletion kotlin/lib/src/main/kotlin/SvixHttpClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ internal constructor(
if (res.code in 200..299) {
return Json.decodeFromString<Res>(bodyString)
}
throw ApiException("None 200 status code", res.code, bodyString)
throw ApiException("Non 200 status code ${res.code}", res.code, bodyString)
}

suspend fun executeRequestWithRetry(request: Request): Response {
Expand Down
9 changes: 5 additions & 4 deletions kotlin/lib/src/test/com/svix/kotlin/BasicTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import kotlin.test.Test
import kotlin.test.assertEquals
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.JsonPrimitive

class BasicTest {
companion object {
Expand Down Expand Up @@ -38,10 +39,10 @@ class BasicTest {
MessageIn(
eventType = "invoice.paid",
payload = kotlinx.serialization.json.Json.encodeToString(
mapOf<String, Any>(
"id" to "invoice_WF7WtCLFFtd8ubcTgboSFNql",
"status" to "paid",
"attempt" to 2,
mapOf<String, JsonPrimitive>(
"id" to JsonPrimitive("invoice_WF7WtCLFFtd8ubcTgboSFNql"),
"status" to JsonPrimitive("paid"),
"attempt" to JsonPrimitive(2),
)
),
),
Expand Down
4 changes: 2 additions & 2 deletions kotlin/lib/src/test/com/svix/kotlin/WiremockTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ class WiremockTests {
runBlocking { svx.message.create("app1", msg) }

val expectedBody =
"""{"eventType":"event.type","payload":"","transformationsParams":{"headers":{"header-key":"header-val"},"rawPayload":"{\"key\":\"val\",\"key1\":[\"list\"]}"}}"""
"""{"eventType":"event.type","payload":{},"transformationsParams":{"headers":{"header-key":"header-val"},"rawPayload":"{\"key\":\"val\",\"key1\":[\"list\"]}"}}"""
wireMockServer.verify(
1,
postRequestedFor(urlEqualTo("/api/v1/app/app1/msg"))
Expand All @@ -437,7 +437,7 @@ class WiremockTests {
runBlocking { svx.message.create("app1", msg) }

val expectedBody =
"""{"eventType":"event.type","payload":"","transformationsParams":{"rawPayload":"{\"key\":\"val\",\"key1\":[\"list\"]}"}}"""
"""{"eventType":"event.type","payload":{},"transformationsParams":{"rawPayload":"{\"key\":\"val\",\"key1\":[\"list\"]}"}}"""

wireMockServer.verify(
1,
Expand Down
15 changes: 15 additions & 0 deletions kotlin/templates/api_extra/message.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,18 @@ fun messageInRaw(
transformationsParams = JsonObject(transformationsParams),
)
}

@Serializable
private data class MessageInInternal(
val application: ApplicationIn? = null,
val channels: Set<String>? = null,
val eventId: String? = null,
val eventType: String,
@Serializable(with = StringAnyMapSerializer::class)
var payload: Map<String, Any>,
val payloadRetentionHours: Long? = null,
val payloadRetentionPeriod: Long? = null,
val tags: Set<String>? = null,
@Serializable(with = StringAnyMapSerializer::class)
var transformationsParams: Map<String, Any>? = null
)
24 changes: 18 additions & 6 deletions kotlin/templates/api_extra/message_create_body.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
if (messageIn.transformationsParams != null) {
var msgInInternal =
MessageInInternal(
messageIn.application,
messageIn.channels,
messageIn.eventId,
messageIn.eventType,
mapOf(),
messageIn.payloadRetentionHours,
messageIn.payloadRetentionPeriod,
messageIn.tags,
messageIn.transformationsParams,
)
if (msgInInternal.transformationsParams != null) {
// only set rawPayload if not already set
if (messageIn.transformationsParams!!["rawPayload"] == null) {
var trParams = (messageIn.transformationsParams as Map<String, Any>).toMutableMap();
if (msgInInternal.transformationsParams!!["rawPayload"] == null) {
var trParams =
(msgInInternal.transformationsParams as Map<String, Any>).toMutableMap()
trParams["rawPayload"] = messageIn.payload
messageIn.transformationsParams = trParams.toMap()
msgInInternal.transformationsParams = trParams.toMap()
}
} else {
val trParams = mapOf("rawPayload" to messageIn.payload)
messageIn.transformationsParams = trParams
msgInInternal.transformationsParams = trParams
}
messageIn.payload = ""
Loading

0 comments on commit eb21ef9

Please sign in to comment.