diff --git a/README.md b/README.md index c3b35bfb9..872070a7f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# th2 common library (Java) (5.13.0) +# th2 common library (Java) (5.13.1) ## Usage @@ -511,6 +511,11 @@ dependencies { ## Release notes +### 5.13.1-dev + ++ Provided ability to set either of raw body of several dody data to `Event` builder ++ Updated th2 gradle plugin `0.0.8` + ### 5.13.0-dev + Added functionality for publisher confirmations to mitigate network issues for message producers. diff --git a/build.gradle b/build.gradle index fce8d0e0f..0a7562505 100644 --- a/build.gradle +++ b/build.gradle @@ -1,8 +1,8 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { - id "com.exactpro.th2.gradle.base" version "0.0.6" - id "com.exactpro.th2.gradle.publish" version "0.0.6" + id "com.exactpro.th2.gradle.base" version "0.0.8" + id "com.exactpro.th2.gradle.publish" version "0.0.8" id "org.jetbrains.kotlin.jvm" version "$kotlin_version" id 'org.jetbrains.kotlin.kapt' version "$kotlin_version" id "java-library" diff --git a/gradle.properties b/gradle.properties index bc2d53f5a..2c553bc74 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # -release_version=5.13.0 +release_version=5.13.1 kotlin_version=1.8.22 description='th2 common library (Java)' vcs_url=https://github.com/th2-net/th2-common-j diff --git a/src/main/java/com/exactpro/th2/common/event/Event.java b/src/main/java/com/exactpro/th2/common/event/Event.java index dfa7f9306..4a43fd33f 100644 --- a/src/main/java/com/exactpro/th2/common/event/Event.java +++ b/src/main/java/com/exactpro/th2/common/event/Event.java @@ -27,12 +27,14 @@ import com.fasterxml.jackson.module.kotlin.KotlinFeature; import com.fasterxml.jackson.module.kotlin.KotlinModule; import com.google.protobuf.ByteString; +import com.google.protobuf.UnsafeByteOperations; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.ArrayList; import java.util.Collection; @@ -58,6 +60,7 @@ import static org.apache.commons.lang3.StringUtils.defaultIfBlank; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; +import static org.apache.commons.lang3.StringUtils.truncate; //TODO: move to common-utils-j @SuppressWarnings("unused") @@ -80,6 +83,7 @@ public class Event { protected final List subEvents = new ArrayList<>(); protected final List attachedMessageIds = new ArrayList<>(); protected final List body = new ArrayList<>(); + protected byte[] rawBody; protected final Instant startTimestamp; protected Instant endTimestamp; protected String type; @@ -151,13 +155,16 @@ public Event name(String eventName) { * This property value will be appended to the end of event name and added into event body in the {@link #toProto(com.exactpro.th2.common.grpc.EventID)} and {@link #toListProto(com.exactpro.th2.common.grpc.EventID)} methods if this property isn't set * * @return current event - * @throws IllegalStateException if description already set + * @throws IllegalStateException if description already set or raw body is already set */ public Event description(String description) { if (isNotBlank(description)) { if (this.description != null) { throw new IllegalStateException(formatStateException("Description", this.description)); } + if (this.rawBody != null) { + throw new IllegalStateException(formatRawBodyStateException("Description")); + } body.add(0, createMessageBean(description)); this.description = description; } @@ -217,21 +224,53 @@ public Event addSubEvent(Event subEvent) { } /** - * Adds passed body data bodyData + * Set raw body + * Note: you can set either of the whole raw body or several body data + * + * @return current event + * @throws IllegalStateException if raw body is already set or body data list isn't empty + */ + public Event rawBody(byte[] rawBody) { + if (this.rawBody != null) { + throw new IllegalStateException( + formatStateException("Raw body", truncate(new String(this.rawBody, StandardCharsets.UTF_8), 25)) + ); + } + if (!this.body.isEmpty()) { + throw new IllegalStateException( + "Raw body can't be set to event '" + id + "' because body data list isn't empty" + ); + } + this.rawBody = rawBody; + return this; + } + + /** + * Adds passed body data bodyData. + * Note: you can set either of several body data or the whole raw body * * @return current event + * @throws IllegalStateException if raw body is already set */ public Event bodyData(IBodyData bodyData) { + if (this.rawBody != null) { + throw new IllegalStateException(formatRawBodyStateException("Body data")); + } body.add(requireNonNull(bodyData, "Body data can't be null")); return this; } /** * Adds passed collection of body data + * Note: you can set either of several body data or the whole raw body * * @return current event + * @throws IllegalStateException if raw body is already set */ public Event bodyData(Collection bodyDataCollection) { + if (this.rawBody != null) { + throw new IllegalStateException(formatRawBodyStateException("Body data collection")); + } body.addAll(requireNonNull(bodyDataCollection, "Body data collection cannot be null")); return this; } @@ -361,7 +400,7 @@ private com.exactpro.th2.common.grpc.Event toProto( .setType(defaultIfBlank(type, UNKNOWN_EVENT_TYPE)) .setEndTimestamp(toTimestamp(endTimestamp)) .setStatus(getAggregatedStatus().eventStatus) - .setBody(ByteString.copyFrom(buildBody())); + .setBody(UnsafeByteOperations.unsafeWrap(buildBody())); List problems = new ArrayList<>(); if (parentId != null) { if (!Objects.equals(parentId.getBookName(), eventId.getBookName())) { @@ -559,11 +598,19 @@ public Instant getEndTimestamp() { } protected byte[] buildBody() throws IOException { - return OBJECT_MAPPER.get().writeValueAsBytes(body); + if (rawBody == null) { + return OBJECT_MAPPER.get().writeValueAsBytes(body); + } else { + return rawBody; + } } protected String formatStateException(String fieldName, Object value) { - return fieldName + " in event '" + id + "' already sed with value '" + value + '\''; + return fieldName + " in event '" + id + "' already set with value '" + value + '\''; + } + + protected String formatRawBodyStateException(String fieldName) { + return fieldName + " can't be added to body data of event '" + id + "' because raw body is already set"; } @NotNull diff --git a/src/test/kotlin/com/exactpro/th2/common/event/TestEvent.kt b/src/test/kotlin/com/exactpro/th2/common/event/TestEvent.kt index 071f4e07d..5371e116b 100644 --- a/src/test/kotlin/com/exactpro/th2/common/event/TestEvent.kt +++ b/src/test/kotlin/com/exactpro/th2/common/event/TestEvent.kt @@ -1,5 +1,5 @@ /* - * Copyright 2020-2023 Exactpro (Exactpro Systems Limited) + * Copyright 2020-2024 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,6 +52,8 @@ class TestEvent { private val data = EventUtils.createMessageBean("0123456789".repeat(20)) private val dataSize = MAPPER.writeValueAsBytes(listOf(data)).size private val bigData = EventUtils.createMessageBean("0123456789".repeat(30)) + /** Event build truncates 25 characters of raw body for error message */ + private val rawBody = "Test raw data longer than 25 characters" @Test fun `call the toProto method on a simple event`() { @@ -286,6 +288,7 @@ class TestEvent { } @Test + @Suppress("unused") fun `serializes date time fields`() { class TestBody( val instant: Instant, @@ -313,6 +316,51 @@ class TestEvent { ) } + @Test + fun `add body data when raw body is already set`() { + val event = Event.start() + .rawBody(rawBody.toByteArray()) + assertThrows(IllegalStateException::class.java) { + event.bodyData(data) + }.also { assertEquals("Body data can't be added to body data of event '${event.id}' because raw body is already set", it.message) } + } + + @Test + fun `set raw body again when raw body is already set`() { + val event = Event.start() + .rawBody(rawBody.toByteArray()) + assertThrows(IllegalStateException::class.java) { + event.rawBody(rawBody.toByteArray()) + }.also { assertEquals("Raw body in event '${event.id}' already set with value '${rawBody.substring(0, 25)}'", it.message) } + } + + @Test + fun `set description when raw body is already set`() { + val event = Event.start() + .rawBody(rawBody.toByteArray()) + assertThrows(IllegalStateException::class.java) { + event.description("test-description") + }.also { assertEquals("Description can't be added to body data of event '${event.id}' because raw body is already set", it.message) } + } + + @Test + fun `set raw body when body data is already added`() { + val event = Event.start() + .bodyData(data) + assertThrows(IllegalStateException::class.java) { + event.rawBody(rawBody.toByteArray()) + }.also { assertEquals("Raw body can't be set to event '${event.id}' because body data list isn't empty", it.message) } + } + + @Test + fun `set raw body when description is already set`() { + val event = Event.start() + .description("test-description") + assertThrows(IllegalStateException::class.java) { + event.rawBody(rawBody.toByteArray()) + }.also { assertEquals("Raw body can't be set to event '${event.id}' because body data list isn't empty", it.message) } + } + @TestFactory fun `book mismatch between attached message and event`(): Collection { val event = Event.start()