diff --git a/.github/workflows/build-dev-release.yml b/.github/workflows/build-dev-release.yml new file mode 100644 index 0000000..b438d11 --- /dev/null +++ b/.github/workflows/build-dev-release.yml @@ -0,0 +1,15 @@ +name: Build and publish dev release Docker image to Github Container Registry ghcr.io + +on: workflow_dispatch + +jobs: + build: + uses: th2-net/.github/.github/workflows/compound-java.yml@main + with: + build-target: 'Docker' + devRelease: true + createTag: true + docker-username: ${{ github.actor }} + secrets: + docker-password: ${{ secrets.GITHUB_TOKEN }} + nvd-api-key: ${{ secrets.NVD_APIKEY }} \ No newline at end of file diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/build-release.yml similarity index 53% rename from .github/workflows/docker-publish.yml rename to .github/workflows/build-release.yml index cec50a6..dcf70be 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/build-release.yml @@ -1,20 +1,15 @@ -name: Build and publish Docker distributions to Github Container Registry ghcr.io +name: Build and publish release Docker image to Github Container Registry ghcr.io -on: - push: - branches: - - master - - version-* - paths: - - gradle.properties -# - package_info.json +on: workflow_dispatch jobs: - build-job: + build: uses: th2-net/.github/.github/workflows/compound-java.yml@main with: build-target: 'Docker' + devRelease: false + createTag: true docker-username: ${{ github.actor }} secrets: docker-password: ${{ secrets.GITHUB_TOKEN }} - \ No newline at end of file + nvd-api-key: ${{ secrets.NVD_APIKEY }} \ No newline at end of file diff --git a/.github/workflows/dev-docker-publish.yml b/.github/workflows/build-sanpshot.yml similarity index 75% rename from .github/workflows/dev-docker-publish.yml rename to .github/workflows/build-sanpshot.yml index eab42dc..9366a44 100644 --- a/.github/workflows/dev-docker-publish.yml +++ b/.github/workflows/build-sanpshot.yml @@ -1,4 +1,4 @@ -name: Dev build and publish Docker distributions to Github Container Registry ghcr.io +name: Build and publish Docker image to Github Container Registry ghcr.io on: push: @@ -9,7 +9,6 @@ on: paths-ignore: - README.md - jobs: build-job: uses: th2-net/.github/.github/workflows/compound-java-dev.yml@main @@ -18,3 +17,4 @@ jobs: docker-username: ${{ github.actor }} secrets: docker-password: ${{ secrets.GITHUB_TOKEN }} + nvd-api-key: ${{ secrets.NVD_APIKEY }} \ No newline at end of file diff --git a/.github/workflows/ci-unwelcome-words.yml b/.github/workflows/ci-unwelcome-words.yml index 686d0fd..4e5f3a6 100644 --- a/.github/workflows/ci-unwelcome-words.yml +++ b/.github/workflows/ci-unwelcome-words.yml @@ -7,17 +7,17 @@ jobs: test: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 - with: - ref: ${{ github.sha }} - - name: Checkout tool - uses: actions/checkout@v3 - with: - repository: exactpro-th2/ci-github-action - ref: master - token: ${{ secrets.PAT_CI_ACTION }} - path: ci-github-action - - name: Run CI action - uses: ./ci-github-action - with: - ref: ${{ github.sha }} \ No newline at end of file + - uses: actions/checkout@v4 + with: + ref: ${{ github.sha }} + - name: Checkout tool + uses: actions/checkout@v4 + with: + repository: exactpro-th2/ci-github-action + ref: master + token: ${{ secrets.PAT_CI_ACTION }} + path: ci-github-action + - name: Run CI action + uses: ./ci-github-action + with: + ref: ${{ github.sha }} diff --git a/.github/workflows/dev-release-docker-publish.yml b/.github/workflows/dev-release-docker-publish.yml deleted file mode 100644 index 54c4d76..0000000 --- a/.github/workflows/dev-release-docker-publish.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Build and publish dev-release Java distributions to sonatype. - -on: - workflow_dispatch: - push: - tags: - - \d+.\d+.\d+-dev - -jobs: - build-job: - uses: th2-net/.github/.github/workflows/compound-java.yml@main - with: - build-target: 'Docker' - docker-username: ${{ github.actor }} - devRelease: true - secrets: - docker-password: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/README.md b/README.md index fdf411d..eeb37ac 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# th2-conn-dirty-fix (1.6.1) +# th2-conn-dirty-fix (1.7.0) This microservice allows sending and receiving messages via FIX protocol @@ -335,6 +335,14 @@ spec: # Changelog +## 1.7.0 +* Added support for th2 transport protocol +* Added configuration option for non-default book per session. +* Migrated to th2 gradle plugin `0.0.8` +* Updated: + * common: `5.13.1-dev` + * conn-dirty-tcp-core: `3.6.0-dev` + ## 1.6.1 * Channel subscriptions recovery on failure diff --git a/build.gradle b/build.gradle index 7f274f4..8c930be 100644 --- a/build.gradle +++ b/build.gradle @@ -1,27 +1,18 @@ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import com.github.jk1.license.filter.LicenseBundleNormalizer -import com.github.jk1.license.render.JsonReportRenderer plugins { - id 'java' + id "application" + id "com.exactpro.th2.gradle.component" version "0.0.8" id 'org.jetbrains.kotlin.jvm' version '1.8.22' - id 'com.palantir.docker' version '0.25.0' - id "org.owasp.dependencycheck" version "9.0.9" - id "com.gorylenko.gradle-git-properties" version "2.4.1" - id 'com.github.jk1.dependency-license-report' version '2.5' - id "de.undercouch.download" version "5.6.0" + id "org.jetbrains.kotlin.kapt" version "1.8.22" } -apply plugin: 'application' -apply plugin: 'com.palantir.docker' -apply plugin: 'kotlin-kapt' - group 'com.exactpro.th2' version release_version -sourceCompatibility = 11 -targetCompatibility = 11 +kotlin { + jvmToolchain(11) +} repositories { mavenCentral() @@ -45,14 +36,12 @@ repositories { } dependencies { - api platform('com.exactpro.th2:bom:4.6.1') - - implementation("com.exactpro.th2:common:5.10.0-dev") { + implementation("com.exactpro.th2:common:5.13.1-dev") { exclude group: 'com.exactpro.th2', module: 'task-utils' } implementation "com.exactpro.th2:common-utils:2.2.3-dev" implementation 'com.exactpro.th2:netty-bytebuf-utils:0.0.1' - implementation 'com.exactpro.th2:conn-dirty-tcp-core:3.5.0-dev' + implementation'com.exactpro.th2:conn-dirty-tcp-core:3.6.0-dev' implementation 'com.exactpro.th2:grpc-lw-data-provider:2.3.1-dev' implementation 'org.slf4j:slf4j-api' @@ -66,7 +55,7 @@ dependencies { implementation 'com.fasterxml.jackson.core:jackson-databind' implementation 'com.fasterxml.jackson.module:jackson-module-kotlin' - testImplementation 'org.mockito:mockito-all:1.10.19' + testImplementation 'org.mockito:mockito-core:5.12.0' testImplementation 'org.jetbrains.kotlin:kotlin-test-junit5:1.8.22' annotationProcessor 'com.google.auto.service:auto-service:1.1.1' @@ -81,61 +70,8 @@ application { mainClass.set('com.exactpro.th2.conn.dirty.tcp.core.Main') } -applicationName = 'service' - -distTar { - archiveFileName.set("${applicationName}.tar") -} - -dockerPrepare { - dependsOn distTar -} - -docker { - copySpec.from(tarTree("$buildDir/distributions/${applicationName}.tar")) -} - -tasks.withType(KotlinCompile).configureEach { - compilerOptions { - jvmTarget.set(JvmTarget.JVM_11) - } -} - dependencyCheck { - formats=['SARIF', 'JSON', 'HTML'] - failBuildOnCVSS=5 suppressionFile="suppressions.xml" - //FIXME: we should check all used dependencies skipConfigurations = ['kapt', 'kaptClasspath_kaptKotlin', 'kaptTest', 'kaptTestFixtures', 'annotationProcessor'] - analyzers { - assemblyEnabled = false - nugetconfEnabled = false - nodeEnabled = false - } -} - -dependencyLocking { - lockAllConfigurations() -} - -licenseReport { - def licenseNormalizerBundlePath = "$buildDir/license-normalizer-bundle.json" - - if (!file(licenseNormalizerBundlePath).exists()) { - download.run { - src 'https://raw.githubusercontent.com/th2-net/.github/main/license-compliance/gradle-license-report/license-normalizer-bundle.json' - dest "$buildDir/license-normalizer-bundle.json" - overwrite false - } - } - - filters = [ - new LicenseBundleNormalizer(licenseNormalizerBundlePath, false) - ] - renderers = [ - new JsonReportRenderer('licenses.json', false), - ] - excludeOwnGroup = false - allowedLicensesFile = new URL("https://raw.githubusercontent.com/th2-net/.github/main/license-compliance/gradle-license-report/allowed-licenses.json") } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 276213d..538334b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ -release_version=1.6.1 +release_version=1.7.0 description='Dirty-TCP client' vcs_url=https://github.com/th2-net/th2-conn-dirty-fix \ No newline at end of file diff --git a/src/test/java/com/exactpro/th2/FixHandlerSendTimeoutTest.java b/src/test/java/com/exactpro/th2/FixHandlerSendTimeoutTest.java index 5e81642..938e66c 100644 --- a/src/test/java/com/exactpro/th2/FixHandlerSendTimeoutTest.java +++ b/src/test/java/com/exactpro/th2/FixHandlerSendTimeoutTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Exactpro (Exactpro Systems Limited) + * Copyright 2023-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. @@ -22,6 +22,7 @@ import com.exactpro.th2.conn.dirty.tcp.core.api.IChannel; import com.exactpro.th2.conn.dirty.tcp.core.api.IHandlerContext; import kotlin.Unit; +import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -32,11 +33,10 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeoutException; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyVararg; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; class FixHandlerSendTimeoutTest { @Test @@ -50,7 +50,7 @@ void sendTimeoutOnConnectionOpen() { anyBoolean(), anyLong(), anyInt(), - anyVararg() + any(String[].class) )) .thenReturn(channelMock); Mockito.when(channelMock.open()) @@ -62,22 +62,23 @@ void sendTimeoutOnConnectionOpen() { settings.setMinConnectionTimeoutOnSend(100); Mockito.when(contextMock.getSettings()) .thenReturn(settings); - var fixHandler = new FixHandler(contextMock); - fixHandler.onStart(); - var exception = Assertions.assertThrows(TimeoutException.class, () -> - fixHandler.send(RawMessage.builder() - .setId(MessageId.builder() - .setDirection(Direction.OUTGOING) - .setSessionAlias("test") - .setSequence(1) - .setTimestamp(Instant.now()) - .build()) - .build())); - Assertions.assertEquals( - "could not open connection before timeout 300 mls elapsed", - exception.getMessage(), - "unexpected message" - ); + try(var fixHandler = new FixHandler(contextMock)) { + fixHandler.onStart(); + var exception = Assertions.assertThrows(TimeoutException.class, () -> + fixHandler.send(RawMessage.builder() + .setId(MessageId.builder() + .setDirection(Direction.OUTGOING) + .setSessionAlias("test") + .setSequence(1) + .setTimestamp(Instant.now()) + .build()) + .build())); + Assertions.assertEquals( + "could not open connection before timeout 300 mls elapsed", + exception.getMessage(), + "unexpected message" + ); + } } @Test @@ -91,12 +92,36 @@ void sendTimeoutOnSessionEnabled() { anyBoolean(), anyLong(), anyInt(), - anyVararg() + any(String[].class) )) .thenReturn(channelMock); Mockito.when(channelMock.open()) .thenReturn(CompletableFuture.completedFuture(Unit.INSTANCE)); // completed immediately Mockito.when(channelMock.isOpen()).thenReturn(true); + var settings = createSettings(); + Mockito.when(contextMock.getSettings()) + .thenReturn(settings); + try(var fixHandler = new FixHandler(contextMock)) { + fixHandler.onStart(); + var exception = Assertions.assertThrows(TimeoutException.class, () -> + fixHandler.send(RawMessage.builder() + .setId(MessageId.builder() + .setDirection(Direction.OUTGOING) + .setSessionAlias("test") + .setSequence(1) + .setTimestamp(Instant.now()) + .build()) + .build())); + Assertions.assertEquals( + "session was not established within 300 mls", + exception.getMessage(), + "unexpected message" + ); + } + } + + @NotNull + private static FixHandlerSettings createSettings() { var settings = new FixHandlerSettings(); settings.setPort(42); settings.setHost("localhost"); @@ -113,23 +138,6 @@ void sendTimeoutOnSessionEnabled() { settings.setSessionStartTime(currentTime.plusMinutes(deltaMinutes * 2)); settings.setSessionEndTime(currentTime.plusMinutes(deltaMinutes)); } - Mockito.when(contextMock.getSettings()) - .thenReturn(settings); - var fixHandler = new FixHandler(contextMock); - fixHandler.onStart(); - var exception = Assertions.assertThrows(TimeoutException.class, () -> - fixHandler.send(RawMessage.builder() - .setId(MessageId.builder() - .setDirection(Direction.OUTGOING) - .setSessionAlias("test") - .setSequence(1) - .setTimestamp(Instant.now()) - .build()) - .build())); - Assertions.assertEquals( - "session was not established within 300 mls", - exception.getMessage(), - "unexpected message" - ); + return settings; } } diff --git a/src/test/java/com/exactpro/th2/RecoveryTest.java b/src/test/java/com/exactpro/th2/RecoveryTest.java index 8372737..9399681 100644 --- a/src/test/java/com/exactpro/th2/RecoveryTest.java +++ b/src/test/java/com/exactpro/th2/RecoveryTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Exactpro (Exactpro Systems Limited) + * Copyright 2023-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. @@ -62,7 +62,7 @@ void testSequenceResetInRange() { ) ); Mockito.when(dataProviderService.searchMessageGroups(Mockito.any())).thenAnswer( - x -> ms.searchMessages(x.getArgumentAt(0, MessageGroupsSearchRequest.class)) + x -> ms.searchMessages(x.getArgument(0, MessageGroupsSearchRequest.class)) ); channel = new Channel(settings, dataProviderService); fixHandler = channel.getFixHandler(); @@ -93,7 +93,7 @@ void testSequenceResetInsideRange() { ) ); Mockito.when(dataProviderService.searchMessageGroups(Mockito.any())).thenAnswer( - x -> ms.searchMessages(x.getArgumentAt(0, MessageGroupsSearchRequest.class)) + x -> ms.searchMessages(x.getArgument(0, MessageGroupsSearchRequest.class)) ); channel = new Channel(settings, dataProviderService); fixHandler = channel.getFixHandler(); @@ -153,7 +153,7 @@ void testSequenceResetOutOfRange() { ) ); Mockito.when(dataProviderService.searchMessageGroups(Mockito.any())).thenAnswer( - x -> ms.searchMessages(x.getArgumentAt(0, MessageGroupsSearchRequest.class)) + x -> ms.searchMessages(x.getArgument(0, MessageGroupsSearchRequest.class)) ); channel = new Channel(settings, dataProviderService); fixHandler = channel.getFixHandler(); @@ -185,7 +185,7 @@ void testSequenceResetAdminMessages() { ) ); Mockito.when(dataProviderService.searchMessageGroups(Mockito.any())).thenAnswer( - x -> ms.searchMessages(x.getArgumentAt(0, MessageGroupsSearchRequest.class)) + x -> ms.searchMessages(x.getArgument(0, MessageGroupsSearchRequest.class)) ); channel = new Channel(settings, dataProviderService); fixHandler = channel.getFixHandler(); @@ -203,7 +203,7 @@ void testSequenceResetAdminMessages() { ByteBuf resendRequest = Unpooled.wrappedBuffer("8=FIXT.1.1\u00019=73\u000135=2\u000134=2\u000149=client\u000156=server\u000150=trader\u000152=2014-12-22T10:15:30Z\u00017=1\u000116=5\u000110=226\u0001".getBytes(StandardCharsets.UTF_8)); fixHandler.onIncoming(channel, resendRequest); - // sequence reset for meesages from 1 to 3 ( 1, 2 - missing, 3 - admin ) + // sequence reset for messages from 1 to 3 ( 1, 2 - missing, 3 - admin ) ByteBuf seqReset1 = channel.getQueue().get(1); assertEquals(findField(seqReset1, MSG_TYPE_TAG).getValue(), MSG_TYPE_SEQUENCE_RESET); assertEquals(Integer.parseInt(findField(seqReset1, MSG_SEQ_NUM_TAG).getValue()), 1); @@ -214,7 +214,7 @@ void testSequenceResetAdminMessages() { assertEquals(Integer.parseInt(findField(message, MSG_SEQ_NUM_TAG).getValue()), 4); assertEquals(findField(message, POSS_DUP_TAG).getValue(), "Y"); - // sequence reset for meesages from 1 to 3 ( 1, 2 - missing, 3 - admin ) + // sequence reset for messages from 1 to 3 ( 1, 2 - missing, 3 - admin ) ByteBuf seqReset2 = channel.getQueue().get(3); assertEquals(findField(seqReset2, MSG_TYPE_TAG).getValue(), MSG_TYPE_SEQUENCE_RESET); assertEquals(Integer.parseInt(findField(seqReset2, MSG_SEQ_NUM_TAG).getValue()), 5); @@ -244,7 +244,7 @@ void allMessagesMissed() { ByteBuf resendRequest = Unpooled.wrappedBuffer("8=FIXT.1.1\u00019=73\u000135=2\u000134=2\u000149=client\u000156=server\u000150=trader\u000152=2014-12-22T10:15:30Z\u00017=1\u000116=5\u000110=226\u0001".getBytes(StandardCharsets.UTF_8)); fixHandler.onIncoming(channel, resendRequest); - // sequence reset for meesages from 1 to 3 ( 1, 2 - missing, 3 - admin ) + // sequence reset for messages from 1 to 3 ( 1, 2 - missing, 3 - admin ) ByteBuf seqReset = channel.getQueue().get(1); assertEquals(findField(seqReset, MSG_TYPE_TAG).getValue(), MSG_TYPE_SEQUENCE_RESET); assertEquals(Integer.parseInt(findField(seqReset, MSG_SEQ_NUM_TAG).getValue()), 1);