Repository: stargate/stargate
Issue: GET by primary key with frozen<udt> using REST V1 is returning 500 error

There are two issues here:
1.  seems that having frozen<some udt> is causing 500
2. after removing problematic column 500 became 400 without error in stargate.log

```
http 'http://10.101.37.135:8082/v1/keyspaces/ks1/tables/table4/rows/8275984804989873180;5276317386634354038;28639;1005107841;1544552259' x-cassandra-token:$TOKEN
HTTP/1.1 500 Internal Server Error
Content-Length: 55
Content-Type: application/json
Date: Tue, 06 Oct 2020 11:59:06 GMT

{
    "code": 500, 
    "description": "Server error: Unknown type"
}
```
Schema for ks1.table4:
```
CREATE TABLE ks1.table4 (
    pk0 varint,
    pk1 varint,
    pk2 smallint,
    pk3 int,
    pk4 int,
    ck0 smallint,
    ck1 text,
    col0 frozen<udt_167785279>,
    col1 date,
    col10 map<tinyint, double>,
    col11 float,
    col2 timestamp,
    col3 timeuuid,
    col4 bigint,
    col5 ascii,
    col6 map<tinyint, varint>,
    col7 float,
    col8 duration,
    col9 map<ascii, decimal>,
    PRIMARY KEY ((pk0, pk1, pk2, pk3, pk4), ck0, ck1)
```
in the Stargate logs:
```
10.151.0.16 - - [06/Oct/2020:11:59:06 +0000] "GET /v1/keyspaces/ks1/tables/table4/rows/8275984804989873180;5276317386634354038;28639;1005107841;1544552259 HTTP/1.1" 500 55 "-" "HTTPie/0.9.2" 15
ERROR [2020-10-06 11:59:06,152] io.stargate.web.resources.RequestHandler: Error when executing request
! java.lang.UnsupportedOperationException: Unknown type
! at io.stargate.db.datastore.Row.getValue(Row.java:272)
! at io.stargate.db.datastore.Row.getValue(Row.java:203)
! at io.stargate.web.resources.Converters.row2Map(Converters.java:75)
! at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
! at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1384)
! at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
! at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
! at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
! at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
! at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:566)
! at io.stargate.web.resources.RowResource.lambda$getOne$0(RowResource.java:100)
! at io.stargate.web.resources.RequestHandler.handle(RequestHandler.java:37)
! at io.stargate.web.resources.RowResource.getOne(RowResource.java:85)
! at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
! at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
! at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
! at java.lang.reflect.Method.invoke(Method.java:498)
! at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory.lambda$static$0(ResourceMethodInvocationHandlerFactory.java:52)
! at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:124)
! at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:167)
! at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:176)
! at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:79)
! at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:469)
! at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:391)
! at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:80)
! at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:253)
! at org.glassfish.jersey.internal.Errors$1.call(Errors.java:248)
! at org.glassfish.jersey.internal.Errors$1.call(Errors.java:244)
! at org.glassfish.jersey.internal.Errors.process(Errors.java:292)
! at org.glassfish.jersey.internal.Errors.process(Errors.java:274)
! at org.glassfish.jersey.internal.Errors.process(Errors.java:244)
! at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:265)
! at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:232)
! at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:680)
! at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:394)
! at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:346)
! at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:366)
! at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:319)
! at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:205)
! at io.dropwizard.jetty.NonblockingServletHolder.handle(NonblockingServletHolder.java:50)
! at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1617)
! at io.dropwizard.servlets.ThreadNameFilter.doFilter(ThreadNameFilter.java:35)
! at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1604)
! at io.dropwizard.jersey.filter.AllowedMethodsFilter.handle(AllowedMethodsFilter.java:47)
! at io.dropwizard.jersey.filter.AllowedMethodsFilter.doFilter(AllowedMethodsFilter.java:41)
! at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1604)
! at org.eclipse.jetty.servlets.CrossOriginFilter.handle(CrossOriginFilter.java:319)
! at org.eclipse.jetty.servlets.CrossOriginFilter.doFilter(CrossOriginFilter.java:273)
! at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1604)
! at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:545)
! at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233)
! at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1297)
! at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:188)
! at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:485)
! at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:186)
! at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1212)
! at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
! at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)
! at com.codahale.metrics.jetty9.InstrumentedHandler.handle(InstrumentedHandler.java:249)
! at io.dropwizard.jetty.RoutingHandler.handle(RoutingHandler.java:52)
! at org.eclipse.jetty.server.handler.gzip.GzipHandler.handle(GzipHandler.java:767)
! at org.eclipse.jetty.server.handler.RequestLogHandler.handle(RequestLogHandler.java:54)
! at org.eclipse.jetty.server.handler.StatisticsHandler.handle(StatisticsHandler.java:173)
! at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)
! at org.eclipse.jetty.server.Server.handle(Server.java:500)
! at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:383)
! at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:547)
! at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:375)
! at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:270)
! at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311)
! at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103)
! at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:117)
! at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:336)
! at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:313)
! at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:171)
! at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:129)
! at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:388)
! at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:806)
! at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:938)
! at java.lang.Thread.run(Thread.java:748)
```

To check which column is causing issues I have removed `col0`.
And here comes a surprise - I started to get 400 after this removal:
```
http 'http://10.101.37.135:8082/v1/keyspaces/ks1/tables/table4/rows/8275984804989873180;5276317386634354038;28639;1005107841;1544552259' x-cassandra-token:$TOKEN
HTTP/1.1 400 Bad Request
Content-Length: 47
Content-Type: application/json
Date: Tue, 06 Oct 2020 12:01:19 GMT

{
    "code": 400, 
    "message": "Unable to process JSON"
}
```


Changed files:
restapi/src/main/java/io/stargate/web/resources/Converters.java, testing/src/main/java/io/stargate/it/http/RestApiv1Test.java

diff --git a/restapi/src/main/java/io/stargate/web/resources/Converters.java b/restapi/src/main/java/io/stargate/web/resources/Converters.java
index 37e5d29a..2f21cb43 100644
--- a/restapi/src/main/java/io/stargate/web/resources/Converters.java
+++ b/restapi/src/main/java/io/stargate/web/resources/Converters.java
@@ -115,6 +115,20 @@ public class Converters {
       if (value instanceof Object[]) {
         value = Arrays.asList((Object[]) value);
       }
+      if (value instanceof UdtValue) {
+        UdtValue udtValue = (UdtValue) value;
+        com.datastax.oss.driver.api.core.type.UserDefinedType udtType = udtValue.getType();
+        Map<String, Object> jsonObject = Maps.newLinkedHashMapWithExpectedSize(udtValue.size());
+        for (int i = 0; i < udtValue.size(); i++) {
+          CqlIdentifier fieldName = udtType.getFieldNames().get(i);
+          Object internalValue = udtValue.getObject(fieldName);
+          if (internalValue instanceof Object[]) {
+            internalValue = Arrays.asList((Object[]) internalValue);
+          }
+          jsonObject.put(fieldName.asInternal(), internalValue);
+        }
+        value = jsonObject;
+      }
       map.put(column.name(), value);
     }
     return map;
diff --git a/testing/src/main/java/io/stargate/it/http/RestApiv1Test.java b/testing/src/main/java/io/stargate/it/http/RestApiv1Test.java
index ac8260b5..81c0ce10 100644
--- a/testing/src/main/java/io/stargate/it/http/RestApiv1Test.java
+++ b/testing/src/main/java/io/stargate/it/http/RestApiv1Test.java
@@ -1065,6 +1065,37 @@ public class RestApiv1Test extends BaseIntegrationTest {
         HttpStatus.SC_BAD_REQUEST);
   }
 
+  @Test
+  public void queryWithUdtCol() throws IOException {
+    String tableName = "tbl_query_" + System.currentTimeMillis();
+    // createTable(tableName);
+    session.execute("create type " + keyspace + ".udt_167785279 (a int, b int)");
+    session.execute(
+        "create table "
+            + keyspace
+            + "."
+            + tableName
+            + " (   pk0 varint,     pk1 varint,     pk2 smallint,     pk3 int,     pk4 int,     ck0 smallint,     ck1 text,     col0 frozen<udt_167785279>,     col1 date,     col10 map<tinyint, double>,     col11 float,     col2 timestamp,     col3 timeuuid,     col4 bigint,     col5 ascii,     col6 map<tinyint, varint>,     col7 float,     col8 duration,     col9 map<ascii, decimal>,     PRIMARY KEY ((pk0, pk1, pk2, pk3, pk4), ck0, ck1))");
+    session.execute(
+        "insert into "
+            + keyspace
+            + "."
+            + tableName
+            + " (pk0,pk1,pk2,pk3,pk4,ck0,ck1,col0) values(8275984804989873180, 5276317386634354038, 28639,1005107841,1544552257, 10,'a',{a:1, b:2})");
+
+    String body =
+        RestUtils.get(
+            authToken,
+            String.format(
+                "%s:8082/v1/keyspaces/%s/tables/%s/rows/8275984804989873180;5276317386634354038;28639;1005107841;1544552257",
+                host, keyspace, tableName),
+            HttpStatus.SC_OK);
+
+    RowResponse rowResponse = objectMapper.readValue(body, RowResponse.class);
+    assertThat(rowResponse.getCount()).isEqualTo(1);
+    assertThat(rowResponse.getRows().get(0).get("col0")).isNotNull();
+  }
+
   @Test
   public void deleteRowWithClustering() throws IOException {
     String tableName = "tbl_deleterows_clustering_" + System.currentTimeMillis();