diff --git a/expressjs/src/routes/events/endpoint.js b/expressjs/src/routes/events/endpoint.js index 7c6f76e..f917c07 100644 --- a/expressjs/src/routes/events/endpoint.js +++ b/expressjs/src/routes/events/endpoint.js @@ -2,11 +2,11 @@ const { HTTP } = require('cloudevents') const openapi = require('../../lib/openapi') const EventStore = require('./store') const devdata = require('./devdata') -const Printer = require('./printer') +const PrettyPrinter = require('./printer') const { log } = require('../../lib/logging') const store = new EventStore() -const printer = new Printer() +const printer = new PrettyPrinter() /** * @typedef {import('express').Express} Express @@ -71,7 +71,7 @@ function recv(req, res) { */ function recvEvent(ce) { const out = printer.print(ce) - log.info('Received:\n%s', out) + log.info('Received event:\n%s', out) ce.validate() store.add(ce) } diff --git a/expressjs/src/routes/events/printer.js b/expressjs/src/routes/events/printer.js index 9e1049c..26803ed 100644 --- a/expressjs/src/routes/events/printer.js +++ b/expressjs/src/routes/events/printer.js @@ -2,7 +2,7 @@ * @typedef {import('cloudevents').CloudEvent} CloudEvent */ -class Printer { +class PrettyPrinter { /** * Prints a CloudEvent in human-readable format. @@ -19,7 +19,7 @@ class Printer { } let buf = '☁️ cloudevents.Event\n' buf += `Validation: ${valid}\n` - const attr = attribites(ce) + const attr = attributes(ce) buf += printAttr(attr) buf += printExts(ce, attr) buf += printData(ce) @@ -33,7 +33,7 @@ class Printer { * @returns {Object} - the CloudEvent attributes * @private */ -function attribites(ce) { +function attributes(ce) { const attr = { specversion: ce.specversion, type: ce.type, @@ -149,4 +149,4 @@ function indent(str, numOfIndents, spacesPerIndent) { : txt } -module.exports = Printer +module.exports = PrettyPrinter diff --git a/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/Presenter.java b/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/Presenter.java new file mode 100644 index 0000000..d02655b --- /dev/null +++ b/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/Presenter.java @@ -0,0 +1,110 @@ +package com.redhat.openshift.knative.showcase.events; + +import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.cloudevents.CloudEvent; +import io.cloudevents.jackson.JsonFormat; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; +import java.io.IOException; +import java.io.Serializable; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +@ApplicationScoped +class Presenter { + + private static final int INDENT_SIZE = 2; + private final ObjectMapper mapper; + + @Inject + Presenter(ObjectMapper mapper) { + this.mapper = mapper; + } + + byte[] asJson(CloudEvent ce) { + var serializer = new JsonFormat(); + return serializer.serialize(ce); + } + + CharSequence asHumanReadable(CloudEvent ce) { + try { + var buf = new StringBuilder("☁️ cloudevents.Event\n"); + buf.append("Validation: valid\n"); + buf.append(printAttr(attributes(ce))); + buf.append(printExts(ce)); + buf.append(printData(ce)); + return buf; + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private Map attributes(CloudEvent ce) { + return ce.getAttributeNames() + .stream() + .map(name -> Map.entry(name, (Serializable) Objects.requireNonNull(ce.getAttribute(name)))) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } + + private CharSequence printAttr(Map attr) { + var buf = new StringBuilder(); + if (!attr.isEmpty()) { + buf.append("Context Attributes,\n"); + attr.forEach((key, value) -> + buf.append(String.format(" %s: %s%n", key, value))); + } + return buf; + } + + private CharSequence printExts(CloudEvent ce) { + var buf = new StringBuilder(); + var exts = extensions(ce); + if (!exts.isEmpty()) { + buf.append("Extensions,\n"); + exts.forEach((key, value) -> + buf.append(String.format(" %s: %s%n", key, value))); + } + return buf; + } + + private Map extensions(CloudEvent ce) { + return ce.getExtensionNames() + .stream() + .map(name -> Map.entry(name, (Serializable) Objects.requireNonNull(ce.getExtension(name)))) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } + + private CharSequence printData(CloudEvent ce) throws IOException { + var buf = new StringBuilder(); + var data = ce.getData(); + if (data != null) { + buf.append("Data,\n"); + var contentType = ce.getDataContentType(); + assert contentType != null; + + if ("application/json".equals(contentType)) { + var json = mapper.readValue(data.toBytes(), Object.class); + var writer = mapper.writer(new DefaultPrettyPrinter()); + var repr = writer.writeValueAsString(json) + .indent(INDENT_SIZE) + .replace("\" : ", "\": "); + buf.append(repr).append("\n"); + return buf; + } + var repr = data.toString(); + var types = List.of("text", "xml", "html", "csv", "json", "yaml"); + if (types.stream().anyMatch(contentType::contains)) { + repr = new String(data.toBytes(), StandardCharsets.UTF_8); + } + buf.append(repr.indent(INDENT_SIZE)).append("\n"); + return buf; + } + return buf; + } +} diff --git a/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/Rest.java b/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/Rest.java index e1a2505..18e2d92 100644 --- a/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/Rest.java +++ b/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/Rest.java @@ -1,12 +1,12 @@ package com.redhat.openshift.knative.showcase.events; import io.cloudevents.CloudEvent; -import io.cloudevents.jackson.JsonFormat; import io.smallrye.mutiny.Multi; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; import javax.ws.rs.Path; @Path("") @@ -15,17 +15,24 @@ class Rest implements Endpoint { private static final Logger LOGGER = LoggerFactory.getLogger(Rest.class); private final EventStore events = new EventStore(); + private final Presenter presenter; + + @Inject + Rest(Presenter presenter) { + this.presenter = presenter; + } @Override public Multi events() { return events.stream() - .map(Rest::workaroundQuarkus31587); + .map(this::workaroundQuarkus31587); } @Override public void receive(CloudEvent event) { + var he = presenter.asHumanReadable(event); + LOGGER.debug("Received event:\n{}", he); events.add(event); - LOGGER.debug("Received event: {}", event); } @Override @@ -40,8 +47,7 @@ public void receiveOnIndex(CloudEvent event) { * * TODO: Remove this method once the above issues is fixed. */ - private static byte[] workaroundQuarkus31587(CloudEvent event) { - var serializer = new JsonFormat(); - return serializer.serialize(event); + private byte[] workaroundQuarkus31587(CloudEvent event) { + return presenter.asJson(event); } }