@BeforeSuite
  public void setUp() throws Exception {
    FieldDependencyBuilder.FieldDependency fieldDependency = new FieldDependencyBuilder().build();
    apiKeyService = new InMemoryApiKeyService();
    metastore = new InMemoryMetastore(apiKeyService);

    SchemaChecker schemaChecker = new SchemaChecker(metastore, fieldDependency);
    eventDeserializer =
        new JsonEventDeserializer(
            metastore, apiKeyService, new TestingConfigManager(), schemaChecker, fieldDependency);
    EventListDeserializer eventListDeserializer =
        new EventListDeserializer(apiKeyService, eventDeserializer);

    mapper = JsonHelper.getMapper();
    mapper.registerModule(
        new SimpleModule()
            .addDeserializer(Event.class, eventDeserializer)
            .addDeserializer(EventList.class, eventListDeserializer));
    eventBuilder = new EventBuilder("test", metastore);
  }
  @GET
  @ApiOperation(
      value = "Subscribe Event Stream",
      consumes = "text/event-stream",
      produces = "text/event-stream",
      authorizations = @Authorization(value = "read_key"),
      notes = "Subscribes the event stream.")
  @ApiResponses(value = {@ApiResponse(code = 400, message = "Project does not exist.")})
  @Path("/subscribe")
  @IgnorePermissionCheck
  @IgnoreApi
  public void subscribe(RakamHttpRequest request) {
    if (!Objects.equals(request.headers().get(HttpHeaders.Names.ACCEPT), "text/event-stream")) {
      request
          .response(
              "the response should accept text/event-stream", HttpResponseStatus.NOT_ACCEPTABLE)
          .end();
      return;
    }

    RakamHttpRequest.StreamResponse response = request.streamResponse();

    List<String> data = request.params().get("data");
    if (data == null || data.isEmpty()) {
      response
          .send(
              "result",
              encode(
                  errorMessage("data query parameter is required", HttpResponseStatus.BAD_REQUEST)))
          .end();
      return;
    }

    StreamQuery query;
    try {
      query = JsonHelper.readSafe(data.get(0), StreamQuery.class);
    } catch (IOException e) {
      response
          .send(
              "result",
              encode(errorMessage("json couldn't parsed", HttpResponseStatus.BAD_REQUEST)))
          .end();
      return;
    }
    List<String> api_key = request.params().get("api_key");
    if (api_key == null
        || api_key.isEmpty()
        || !metastore.checkPermission(
            query.project, Metastore.AccessKeyType.READ_KEY, api_key.get(0))) {
      response.send("result", HttpResponseStatus.UNAUTHORIZED.reasonPhrase()).end();
      return;
    }

    List<CollectionStreamQuery> collect;
    try {
      collect =
          query
              .collections
              .stream()
              .map(
                  collection -> {
                    Expression expression = null;
                    try {
                      expression =
                          collection.filter == null
                              ? null
                              : sqlParser.createExpression(collection.filter);
                    } catch (ParsingException e) {
                      ObjectNode obj =
                          errorMessage(
                              format(
                                  "Couldn't parse %s: %s", collection.filter, e.getErrorMessage()),
                              HttpResponseStatus.BAD_REQUEST);
                      request.response(encode(obj)).end();
                      throw e;
                    }
                    return new CollectionStreamQuery(
                        collection.name, expression == null ? null : expression.toString());
                  })
              .collect(Collectors.toList());
    } catch (ParsingException e) {
      return;
    }

    EventStream.EventStreamer subscribe =
        stream.subscribe(
            query.project, collect, query.columns, new StreamResponseAdapter(response));

    eventLoopGroup.schedule(
        new Runnable() {
          @Override
          public void run() {
            if (response.isClosed()) {
              subscribe.shutdown();
            } else {
              subscribe.sync();
              eventLoopGroup.schedule(this, 3, TimeUnit.SECONDS);
            }
          }
        },
        3,
        TimeUnit.SECONDS);
  }