java-sdk icon indicating copy to clipboard operation
java-sdk copied to clipboard

Outbox message should be in the CloudEvent format

Open mcruzdev opened this issue 2 months ago • 4 comments

Context

Using the Outbox API:

public Response createWithOutbox(OrderRequest request) {
        Log.info("Receiving request for creating an Order: " + request);

        String uuid = UUID.randomUUID().toString();

        Order order = new Order(uuid,
                request.items().stream().map(i -> new Order.OrderItem(i.id(), i.name(), i.price())).toList());

        /* outbox.yaml: metadata.name */
        ExecuteStateTransactionRequest transactionRequest = new ExecuteStateTransactionRequest("postgres-outbox");

        List<TransactionalStateOperation<?>> ops = new ArrayList<>();

        Map<String, String> metadata = new HashMap<>();
        metadata.put("datacontenttype", "application/json");

        State<Order> state = new State<>(order.id(), order, null, metadata, new StateOptions(Consistency.STRONG, Concurrency.FIRST_WRITE));

        TransactionalStateOperation<Order> upsertOps = new TransactionalStateOperation<>(
                TransactionalStateOperation.OperationType.UPSERT, state);

        ops.add(upsertOps);

        transactionRequest.setOperations(ops);

        dapr.executeStateTransaction(transactionRequest).block();

        Log.info("Event (order.created) sent to 'rabbitmq' and order saved to the database using Outbox pattern");

        return Response
                .ok(new OrderResponse(order.id(), order.status(), order.items().stream()
                        .map(item -> new OrderRequest.OrderItemRequest(item.id(), item.name(), item.price())).toList()))
                .build();
    }

I am facing a wrong behavior, which is not the same as documented here https://docs.dapr.io/developing-applications/building-blocks/pubsub/pubsub-cloudevents/#dapr-generated-cloudevents-example

Actual behavior

{"data":"{\"id\":\"dd1f1122-cb3a-4dc4-8ab6-e16ff6599a61\",\"status\":\"RECEIVED\",\"items\":[{\"id\":1,\"name\":\"Quarkus Stickers\",\"price\":19.99}]}","datacontenttype":"text/plain","id":"outbox-06af412d-e21b-4151-8f25-9c6c7273a95c","pubsubname":"rabbitmq","source":"orders-api","specversion":"1.0","time":"2025-10-22T18:40:26Z","topic":"order.created","traceid":"00-dc2464c8d9432109a3d1f13469aa739a-ed59c9a3d2829220-01","traceparent":"","tracestate":"","type":"com.dapr.event.sent"}

Expected behavior

{"data": { "id": "dd1f1122-cb3a-4dc4-8ab6-e16ff6599a61","status":"RECEIVED","items":[{"id":1,"name":"Quarkus Stickers","price":19.99}]}","datacontenttype":"application/json","id":"outbox-06af412d-e21b-4151-8f25-9c6c7273a95c","pubsubname":"rabbitmq","source":"orders-api","specversion":"1.0","time":"2025-10-22T18:40:26Z","topic":"order.created","traceid":"00-dc2464c8d9432109a3d1f13469aa739a-ed59c9a3d2829220-01","traceparent":"","tracestate":"","type":"com.dapr.event.sent"}

The data field is coming as string not a object, I tried to use cloudevent.datacontentype but without success too.

mcruzdev avatar Oct 22 '25 18:10 mcruzdev

/assign

mcruzdev avatar Oct 22 '25 18:10 mcruzdev

It is the same issue reported here https://github.com/dapr/dapr/issues/7113 but for .NET client

mcruzdev avatar Oct 22 '25 19:10 mcruzdev

@artur-ciocanu @salaboy @cicoyle @siri-varma I think that I found the problem. The gRPC field is bytes, and is mapped to a ByteString on Java side.

gRPC definition: https://github.com/dapr/dapr/blob/7d652ff7e69e106157f527f4fb7a1b0435537d29/dapr/proto/common/v1/common.proto#L112

ByteString.copyFrom: https://github.com/dapr/java-sdk/blob/6e9ee5adc4a7300ae96822264f9be65458aa0880/sdk/src/main/java/io/dapr/client/DaprClientImpl.java#L858C7-L858C57

It transforms the value to a JSON string format instead a JSON object.

One solution that I have in mind is to map this one on the consumer side (in the framework code, before the user code), but it only solves the consumer, if we look into a database, the data is in JSON string too.

mcruzdev avatar Oct 26 '25 03:10 mcruzdev

@mcruzdev hmm.. let me raise this with the dotnet folks. If someone reported the same in another SDK, does that mean that that we need to change our protos? Wouldn't that be easier than making a change on the SDK?

salaboy avatar Dec 11 '25 13:12 salaboy