Repository: provectus/kafka-ui
Issue: Produce message is not working for topics with enums in schema
Labels: type/bug
scope/backend

**Describe the bug**
Produce message is not working for topics with enums in schema. If you'll try to produce a message for a topic with schema and enum in it. Frontend will fail:

```
 Type key is missing
    at Object.a.default (index.ts:112)
    at m (index.ts:69)
    at Object.a.build [as default] (index.ts:218)
    at a.default (index.ts:15)
    at SendMessage.tsx:67
    at Rl (react-dom.production.min.js:262)
    at a.unstable_runWithPriority (scheduler.production.min.js:18)
    at Gt (react-dom.production.min.js:122)
    at Ll (react-dom.production.min.js:261)
    at react-dom.production.min.js:261
```

The problem is In json schema generator which skip type property for enum fields.


Issue comments:

Pull request review comments:

Patch that resolved the issue:
diff --git a/kafka-ui-api/src/main/java/com/provectus/kafka/ui/util/jsonschema/AvroJsonSchemaConverter.java b/kafka-ui-api/src/main/java/com/provectus/kafka/ui/util/jsonschema/AvroJsonSchemaConverter.java
index ac12109d5a3..707b11dbf68 100644
--- a/kafka-ui-api/src/main/java/com/provectus/kafka/ui/util/jsonschema/AvroJsonSchemaConverter.java
+++ b/kafka-ui-api/src/main/java/com/provectus/kafka/ui/util/jsonschema/AvroJsonSchemaConverter.java
@@ -6,8 +6,6 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Optional;
-import java.util.UUID;
 import java.util.stream.Collectors;
 import org.apache.avro.Schema;
 import reactor.util.function.Tuple2;
@@ -82,9 +80,7 @@ private FieldSchema createUnionSchema(Schema schema, Map<String, FieldSchema> de
             Tuple2::getT2
         ));
 
-    return new ObjectFieldSchema(
-        fields, Collections.emptyList()
-    );
+    return new ObjectFieldSchema(fields, Collections.emptyList(), true);
   }
 
   private FieldSchema createObjectSchema(String name, Schema schema,
diff --git a/kafka-ui-api/src/main/java/com/provectus/kafka/ui/util/jsonschema/ObjectFieldSchema.java b/kafka-ui-api/src/main/java/com/provectus/kafka/ui/util/jsonschema/ObjectFieldSchema.java
index ca3182a7232..3fe1f26fcd0 100644
--- a/kafka-ui-api/src/main/java/com/provectus/kafka/ui/util/jsonschema/ObjectFieldSchema.java
+++ b/kafka-ui-api/src/main/java/com/provectus/kafka/ui/util/jsonschema/ObjectFieldSchema.java
@@ -2,7 +2,10 @@
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.BooleanNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.databind.node.TextNode;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
@@ -12,11 +15,18 @@
 public class ObjectFieldSchema implements FieldSchema {
   private final Map<String, FieldSchema> properties;
   private final List<String> required;
+  private final boolean nullable;
 
   public ObjectFieldSchema(Map<String, FieldSchema> properties,
                            List<String> required) {
+    this(properties, required, false);
+  }
+
+  public ObjectFieldSchema(Map<String, FieldSchema> properties,
+                           List<String> required, boolean nullable) {
     this.properties = properties;
     this.required = required;
+    this.nullable = nullable;
   }
 
   public Map<String, FieldSchema> getProperties() {
@@ -36,7 +46,18 @@ public JsonNode toJsonNode(ObjectMapper mapper) {
             Tuple2::getT2
         ));
     final ObjectNode objectNode = mapper.createObjectNode();
-    objectNode.setAll(new SimpleJsonType(JsonType.Type.OBJECT).toJsonNode(mapper));
+    if (this.nullable) {
+      objectNode.set(
+          "type",
+          mapper.createArrayNode()
+              .add(JsonType.Type.OBJECT.getName())
+              .add(JsonType.Type.NULL.getName())
+      );
+    } else {
+      objectNode.setAll(
+          new SimpleJsonType(JsonType.Type.OBJECT).toJsonNode(mapper)
+      );
+    }
     objectNode.set("properties", mapper.valueToTree(nodes));
     if (!required.isEmpty()) {
       objectNode.set("required", mapper.valueToTree(required));


Test patch:
diff --git a/kafka-ui-api/src/test/java/com/provectus/kafka/ui/util/jsonschema/AvroJsonSchemaConverterTest.java b/kafka-ui-api/src/test/java/com/provectus/kafka/ui/util/jsonschema/AvroJsonSchemaConverterTest.java
index dbe37f5695b..58daa739cf7 100644
--- a/kafka-ui-api/src/test/java/com/provectus/kafka/ui/util/jsonschema/AvroJsonSchemaConverterTest.java
+++ b/kafka-ui-api/src/test/java/com/provectus/kafka/ui/util/jsonschema/AvroJsonSchemaConverterTest.java
@@ -87,7 +87,7 @@ public void avroConvertTest() throws URISyntaxException, JsonProcessingException
         + "{\"$ref\":\"#/definitions/RecordInnerMessage\"}},"
         + "\"required\":[\"record\"],\"definitions\":"
         + "{\"RecordInnerMessage\":{\"type\":\"object\",\""
-        + "properties\":{\"long_text\":{\"type\":\"object\","
+        + "properties\":{\"long_text\":{\"type\":[\"object\", \"null\"],"
         + "\"properties\":{\"string\":{\"type\":\"string\"}}},"
         + "\"array\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},"
         + "\"id\":{\"type\":\"integer\"},\"text\":{\"type\":\"string\"},"
@@ -151,8 +151,8 @@ public void testNullableUnions() throws URISyntaxException, IOException, Process
         "{\"$id\":\"http://example.com/Message\","
         + "\"$schema\":\"https://json-schema.org/draft/2020-12/schema\","
         + "\"type\":\"object\",\"properties\":{\"text\":"
-        + "{\"type\":\"object\",\"properties\":{\"string\":"
-        + "{\"type\":\"string\"}}},\"value\":{\"type\":\"object\","
+        + "{\"type\":[\"object\", \"null\"],\"properties\":{\"string\":"
+        + "{\"type\":\"string\"}}},\"value\":{\"type\":[\"object\", \"null\"],"
         + "\"properties\":{\"string\":{\"type\":\"string\"},"
         + "\"long\":{\"type\":\"integer\"}}}}}";