Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;

import java.util.Collections;
import java.util.List;
import java.util.Map;

Expand Down Expand Up @@ -46,6 +47,7 @@ public static class Field {
private String text;
@SerializedName("rich_text")
private List<RichTextBlock> richText;
private transient List<Message> messages;
private Message message;
private List<Double> number;
private List<String> select;
Expand All @@ -60,6 +62,20 @@ public static class Field {
private List<Integer> timestamp;
private List<LinkField> link;
private List<ReferenceField> reference;

public List<Message> getMessages() {
if (messages == null && message != null) {
return Collections.singletonList(message);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sweeet 🍬 I like this 💯

}
return messages;
}

public void setMessages(List<Message> messages) {
this.messages = messages;
if (messages != null && !messages.isEmpty()) {
this.message = messages.get(0);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure of this, but I think in most cases users will know to use getMessages if they expect multiple messages 🤔

}
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package test_locally.api.model.list;

import com.google.gson.Gson;
import com.slack.api.model.Message;
import com.slack.api.model.list.ListRecord;
import org.junit.Test;
import test_locally.unit.GsonFactory;

import java.util.List;

import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;

public class ListRecordFieldTest {

private final Gson gson = GsonFactory.createSnakeCase();

@Test
public void messageAsObject() {
String json = "{\"id\":\"r1\",\"fields\":[{" +
"\"key\":\"k1\"," + "\"message\":{\"text\":\"hello\",\"ts\":\"1.0\"}" +
"}]}";
ListRecord record = gson.fromJson(json, ListRecord.class);
ListRecord.Field field = record.getFields().get(0);

// Old API works
assertThat(field.getMessage(), is(notNullValue()));
assertThat(field.getMessage().getText(), is("hello"));

// New API works
assertThat(field.getMessages(), is(notNullValue()));
assertThat(field.getMessages().size(), is(1));

assertThat(field.getMessages().get(0).getText(), is("hello"));
}

@Test
public void messageAsArray() {
String json = "{\"id\":\"r2\",\"fields\":[{" +
"\"key\":\"k1\"," +
"\"message\":[" +
" {\"text\":\"first\",\"ts\":\"1.0\"}," +
" {\"text\":\"second\",\"ts\":\"2.0\"}" +
"]" +
"}]}";

ListRecord record = gson.fromJson(json, ListRecord.class);
ListRecord.Field field = record.getFields().get(0);

// Old API returns first
assertThat(field.getMessage(), is(notNullValue()));
assertThat(field.getMessage().getText(), is("first"));

// New API returns all
assertThat(field.getMessages().size(), is(2));

assertThat(field.getMessages().get(0).getText(), is("first"));

assertThat(field.getMessages().get(1).getText(), is("second"));
}

@Test
public void messageAbsent() {
String json = "{\"id\":\"r3\",\"fields\":[{\"key\":\"k1\",\"number\":[1.0]}]}";
ListRecord record = gson.fromJson(json, ListRecord.class);
ListRecord.Field field = record.getFields().get(0);

assertThat(field.getMessage(), is(nullValue()));
assertThat(field.getMessages(), is(nullValue()));
}

@Test
public void messageNull() {
String json = "{\"id\":\"r4\",\"fields\":[{\"key\":\"k1\",\"message\":null}]}";
ListRecord record = gson.fromJson(json, ListRecord.class);
ListRecord.Field field = record.getFields().get(0);

assertThat(field.getMessage(), is(nullValue()));
assertThat(field.getMessages(), is(nullValue()));
}

@Test
public void messageEmptyArray() {
String json = "{\"id\":\"r5\",\"fields\":[{\"key\":\"k1\",\"message\":[]}]}";
ListRecord record = gson.fromJson(json, ListRecord.class);
ListRecord.Field field = record.getFields().get(0);

assertThat(field.getMessage(), is(nullValue()));
assertThat(field.getMessages(), is(notNullValue()));
assertThat(field.getMessages().size(), is(0));
}

@Test
public void serializeSingleMessage() {
String json = "{\"id\":\"r6\",\"fields\":[{\"key\":\"k1\",\"message\":{\"text\":\"hi\"}}]}";
ListRecord record = gson.fromJson(json, ListRecord.class);
String output = gson.toJson(record);

// Round-trips cleanly
ListRecord reparsed = gson.fromJson(output, ListRecord.class);
assertThat(reparsed.getFields().get(0).getMessage().getText(), is("hi"));
}

@Test
public void builder() {
Message msg = new Message();
msg.setText("built");

ListRecord.Field field = ListRecord.Field.builder()
.key("k1")
.message(msg)
.build();

assertThat(field.getMessage().getText(), is("built"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import com.slack.api.model.event.FunctionExecutedEvent;
import com.slack.api.model.event.MessageChangedEvent;
import com.slack.api.util.json.*;
import com.slack.api.model.list.ListRecord;
import com.slack.api.util.json.GsonListRecordFieldFactory;

public class GsonFactory {
private GsonFactory() {
Expand Down Expand Up @@ -42,7 +44,8 @@ public static Gson createSnakeCase(boolean failOnUnknownProperties, boolean unkn
.registerTypeAdapter(Attachment.VideoHtml.class,
new GsonMessageAttachmentVideoHtmlFactory(failOnUnknownProperties))
.registerTypeAdapter(MessageChangedEvent.PreviousMessage.class,
new GsonMessageChangedEventPreviousMessageFactory(failOnUnknownProperties));
new GsonMessageChangedEventPreviousMessageFactory(failOnUnknownProperties))
.registerTypeAdapter(ListRecord.Field.class, new GsonListRecordFieldFactory(failOnUnknownProperties));

if (unknownPropertyDetection) {
return builder.registerTypeAdapterFactory(new UnknownPropertyDetectionAdapterFactory()).create();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.slack.api.util.json;

import com.google.gson.*;
import com.slack.api.model.Message;
import com.slack.api.model.list.ListRecord;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class GsonListRecordFieldFactory implements JsonDeserializer<ListRecord.Field>, JsonSerializer<ListRecord.Field> {

static class NormalizedField extends ListRecord.Field {
}

private final boolean failOnUnknownProperties;

public GsonListRecordFieldFactory() {
this(false);
}

public GsonListRecordFieldFactory(boolean failOnUnknownProperties) {
this.failOnUnknownProperties = failOnUnknownProperties;
}

@Override
public ListRecord.Field deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
// The "message" field can be a single object or an array.
// Normalize to array before deserializing.
if (jsonObject.has("message") && jsonObject.get("message").isJsonArray()) {
JsonArray messageArray = jsonObject.getAsJsonArray("message");
// Store the full array as "messages" and keep first element as "message"
jsonObject.remove("message");
if (messageArray.size() > 0) {
jsonObject.add("message", messageArray.get(0));
}
ListRecord.Field field = context.deserialize(jsonObject, NormalizedField.class);
List<Message> messages = new ArrayList<>();
for (JsonElement element : messageArray) {
messages.add(context.deserialize(element, Message.class));
}
field.setMessages(messages);
return field;
}
// Single object or absent — standard deserialization
ListRecord.Field field = context.deserialize(jsonObject, NormalizedField.class);
if (field.getMessage() != null) {
field.setMessages(Collections.singletonList(field.getMessage()));
}
return field;
}

@Override
public JsonElement serialize(ListRecord.Field src, Type typeOfSrc, JsonSerializationContext context) {
return context.serialize(src, NormalizedField.class);
}
}