From 1fdd8c8b427a932176a568554731b771b71b0af7 Mon Sep 17 00:00:00 2001 From: dgeorgiev Date: Fri, 7 Jun 2024 14:02:15 +0000 Subject: [PATCH 01/20] [WTCH-291] Upgrade Java (8 -> 17) --- .gitlab-ci.yml | 19 +- build.gradle.kts | 3 +- gradle.properties | 4 +- gradle/wrapper/gradle-wrapper.properties | 2 +- samples/build.gradle.kts | 16 -- samples/java8-sample-contract/Dockerfile | 12 - .../java8-sample-contract/build.gradle.kts | 50 ----- .../build_and_push_to_docker.sh | 12 - .../java8/contract/rockps/MainDispatch.java | 22 -- .../rockps/RockPaperScissorsContractImpl.java | 110 --------- .../rockps/api/RockPaperScissorsContract.java | 30 --- .../rockps/domain/game/AnswerType.java | 7 - .../contract/rockps/domain/game/Game.java | 85 ------- .../rockps/domain/game/GameStatus.java | 5 - .../contract/rockps/domain/game/Player.java | 32 --- .../game/request/CreateGameRequest.java | 41 ---- .../domain/game/request/PlayRequest.java | 20 -- .../domain/game/request/RevealRequest.java | 20 -- .../java8/contract/rockps/util/Util.java | 32 --- .../rockps/RockPaperScissorsContractTest.java | 209 ------------------ .../domain/game/AnswerComparatorTest.java | 35 --- .../kotlin-backend-app/build.gradle.kts | 4 +- .../kotlin-contract/build.gradle.kts | 4 +- samples/settings.gradle.kts | 2 - samples/take_root_version_for_build.sh | 2 + .../ContractStateExternalWithCacheTest.kt | 5 +- .../contract/core/state/ContractStateTest.kt | 2 +- 27 files changed, 24 insertions(+), 761 deletions(-) delete mode 100644 samples/java8-sample-contract/Dockerfile delete mode 100644 samples/java8-sample-contract/build.gradle.kts delete mode 100755 samples/java8-sample-contract/build_and_push_to_docker.sh delete mode 100644 samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/MainDispatch.java delete mode 100644 samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/RockPaperScissorsContractImpl.java delete mode 100644 samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/api/RockPaperScissorsContract.java delete mode 100644 samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/domain/game/AnswerType.java delete mode 100644 samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/domain/game/Game.java delete mode 100644 samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/domain/game/GameStatus.java delete mode 100644 samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/domain/game/Player.java delete mode 100644 samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/domain/game/request/CreateGameRequest.java delete mode 100644 samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/domain/game/request/PlayRequest.java delete mode 100644 samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/domain/game/request/RevealRequest.java delete mode 100644 samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/util/Util.java delete mode 100644 samples/java8-sample-contract/src/test/java/my/sample/java8/contract/rockps/RockPaperScissorsContractTest.java delete mode 100644 samples/java8-sample-contract/src/test/java/my/sample/java8/contract/rockps/domain/game/AnswerComparatorTest.java diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 681fc7f..12b6a24 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -11,7 +11,7 @@ include: file: 'common/sonarqube.gitlab-ci.yml' - project: 'integrator1/devops/gitlab-ci-jobs' ref: master - file: 'common/build-gradle.gitlab-ci.yml' + file: 'common/build-gradle.gitlab-ci.yml' stages: - tests @@ -22,15 +22,15 @@ stages: expire_in: 1 week name: "${CI_COMMIT_SHORT_SHA}_${CI_JOB_ID}" reports: - junit: "**/test-results/test/*.xml" + junit: "**/test-results/test/*.xml" paths: - "**/build/reports/detekt/detekt.xml" - "**/test-results/test/*.xml" - "**/build/jacocoReports/test/jacocoTestReport.xml" - - images.json + - images.json - "**/build/libs" -# check code quality +# check code quality code_quality: extends: - .check_code_quality @@ -39,10 +39,11 @@ code_quality: - dev - merge_requests - /^.*rc.*$/ - - /^.*release.*$/ + - /^.*release.*$/ - /^.*hotfix.*$/ - + tests&build: + image: $CI_REGISTRY/integrator/devops/openjdk-17-slim-docker:d837de0d extends: - .test-gradle script: @@ -51,11 +52,12 @@ tests&build: only: - dev - /^.*rc.*$/ - - /^.*release.*$/ + - /^.*release.*$/ - /^.*hotfix.*$/ <<: *artifacts tests&build_for_maven_central: + image: $CI_REGISTRY/integrator/devops/openjdk-17-slim-docker:d837de0d extends: - .test-gradle script: @@ -68,6 +70,7 @@ tests&build_for_maven_central: <<: *artifacts tests-mr: + image: $CI_REGISTRY/integrator/devops/openjdk-17-slim-docker:d837de0d extends: - .test-mr-gradle script: @@ -80,4 +83,4 @@ tests-mr: - git merge origin/$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME - ./gradlew -PweMavenUser=$MAVEN_USER -PweMavenPassword=$MAVEN_PASSWORD check build - docker images --format "{{json . }}" --no-trunc > images.json - <<: *artifacts + <<: *artifacts diff --git a/build.gradle.kts b/build.gradle.kts index 2055f87..6aeeeb2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -288,8 +288,7 @@ configure( "-Xjsr305=strict", "-Xjvm-default=all", // todo move to api only ) - jvmTarget = JavaVersion.VERSION_1_8.toString() - targetCompatibility = JavaVersion.VERSION_1_8.toString() + jvmTarget = JavaVersion.VERSION_17.toString() } } diff --git a/gradle.properties b/gradle.properties index f7109f7..77b3bec 100644 --- a/gradle.properties +++ b/gradle.properties @@ -31,11 +31,11 @@ ioGrpcVersion=1.20.0 # Testing junitVersion=5.8.2 -mockkVersion=1.12.3 +mockkVersion=1.13.11 springMockkVersion=3.1.1 # WE SDK -weNodeClientVersion=1.3.1 +weNodeClientVersion=1.4.2-900453f4-SNAPSHOT # GitHub properties gitHubProject = "waves-enterprise/we-contract-sdk" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ffed3a2..068cdb2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/samples/build.gradle.kts b/samples/build.gradle.kts index f62ea38..6451539 100644 --- a/samples/build.gradle.kts +++ b/samples/build.gradle.kts @@ -1,6 +1,3 @@ -val weMavenUser: String by project -val weMavenPassword: String by project - allprojects { group = "com.wavesenterprise.sdk.samples" version = "0.0.1-SNAPSHOT" @@ -8,18 +5,5 @@ allprojects { repositories { mavenLocal() mavenCentral() - if (weMavenUser != null && weMavenPassword != null) { - maven { - name = "we-snapshots" - url = uri("https://artifacts.wavesenterprise.com/repository/maven-snapshots/") - mavenContent { - snapshotsOnly() - } - credentials { - username = weMavenUser - password = weMavenPassword - } - } - } } } diff --git a/samples/java8-sample-contract/Dockerfile b/samples/java8-sample-contract/Dockerfile deleted file mode 100644 index 6498c66..0000000 --- a/samples/java8-sample-contract/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM openjdk:8-jre-alpine -MAINTAINER Waves Enterprise <> - -ENV JAVA_MEM="-Xmx256M" -ENV JAVA_OPTS="" - -ADD build/libs/*-all.jar app.jar -RUN apk --no-cache add curl - -RUN chmod +x app.jar -RUN eval $SET_ENV_CMD -CMD ["/bin/sh", "-c", "eval ${SET_ENV_CMD} ; java $JAVA_MEM $JAVA_OPTS -jar app.jar"] diff --git a/samples/java8-sample-contract/build.gradle.kts b/samples/java8-sample-contract/build.gradle.kts deleted file mode 100644 index 2ac1c69..0000000 --- a/samples/java8-sample-contract/build.gradle.kts +++ /dev/null @@ -1,50 +0,0 @@ -plugins { - application - java - id("com.github.johnrengelman.shadow") version "7.1.2" -} - -val weContractSdkVersion: String by project -val jacksonVersion: String by project -val junitVersion: String by project -val apacheCommonsCodecVersion: String by project - -dependencies { - implementation("com.wavesenterprise:we-contract-sdk-grpc:$weContractSdkVersion") - implementation("com.wavesenterprise:we-contract-sdk-test:$weContractSdkVersion") - implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jacksonVersion") - implementation("commons-codec:commons-codec:$apacheCommonsCodecVersion") - - testImplementation("org.junit.jupiter:junit-jupiter-api:$junitVersion") - testImplementation("org.junit.jupiter:junit-jupiter-params:$junitVersion") - testImplementation("org.junit.jupiter:junit-jupiter-engine:$junitVersion") -} - -tasks.withType { - useJUnitPlatform() - testLogging { - events = setOf( - org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED, - org.gradle.api.tasks.testing.logging.TestLogEvent.PASSED, - org.gradle.api.tasks.testing.logging.TestLogEvent.SKIPPED - ) - exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL - showExceptions = true - showCauses = true - showStackTraces = true - } -} - -tasks.withType { - manifest { - attributes["Main-Class"] = "my.sample.java8.contract.rockps.MainDispatch" - } -} - -project.setProperty("mainClassName", "my.sample.java8.contract.rockps.MainDispatch") - -java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(8)) - } -} \ No newline at end of file diff --git a/samples/java8-sample-contract/build_and_push_to_docker.sh b/samples/java8-sample-contract/build_and_push_to_docker.sh deleted file mode 100755 index 92ea38d..0000000 --- a/samples/java8-sample-contract/build_and_push_to_docker.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -#$1 -- image -set -e -.././gradlew clean build -docker build . -t $1 -docker push $1 - -inspectResult=$(docker inspect $1 | grep '"Id": "sha256') -imageHash=$(awk -F'"Id": "sha256:|",' '{print $2}' <<< "$inspectResult") - -printf "image - $1 \nimageHash - $imageHash\n" \ No newline at end of file diff --git a/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/MainDispatch.java b/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/MainDispatch.java deleted file mode 100644 index 48b0f92..0000000 --- a/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/MainDispatch.java +++ /dev/null @@ -1,22 +0,0 @@ -package my.sample.java8.contract.rockps; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.json.JsonMapper; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import com.wavesenterprise.sdk.contract.core.dispatch.ContractDispatcher; -import com.wavesenterprise.sdk.contract.grpc.GrpcJacksonContractDispatcherBuilder; - -public class MainDispatch { - public static void main(String[] args) { - ContractDispatcher contractDispatcher = GrpcJacksonContractDispatcherBuilder.builder() - .contractHandlerType(RockPaperScissorsContractImpl.class) - .objectMapper(getObjectMapper()) - .build(); - - contractDispatcher.dispatch(); - } - - private static ObjectMapper getObjectMapper() { - return JsonMapper.builder().addModule(new JavaTimeModule()).build(); - } -} diff --git a/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/RockPaperScissorsContractImpl.java b/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/RockPaperScissorsContractImpl.java deleted file mode 100644 index c36db32..0000000 --- a/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/RockPaperScissorsContractImpl.java +++ /dev/null @@ -1,110 +0,0 @@ -package my.sample.java8.contract.rockps; - -import com.wavesenterprise.sdk.contract.api.annotation.ContractHandler; -import com.wavesenterprise.sdk.contract.api.state.ContractState; -import com.wavesenterprise.sdk.contract.api.state.TypeReference; -import com.wavesenterprise.sdk.contract.api.state.mapping.Mapping; -import com.wavesenterprise.sdk.node.domain.contract.ContractTransaction; -import my.sample.java8.contract.rockps.api.RockPaperScissorsContract; -import my.sample.java8.contract.rockps.domain.game.Game; -import my.sample.java8.contract.rockps.domain.game.GameStatus; -import my.sample.java8.contract.rockps.domain.game.Player; -import my.sample.java8.contract.rockps.domain.game.request.CreateGameRequest; -import my.sample.java8.contract.rockps.domain.game.request.PlayRequest; -import my.sample.java8.contract.rockps.domain.game.request.RevealRequest; -import org.jetbrains.annotations.NotNull; - -import java.time.Instant; -import java.time.OffsetDateTime; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Set; -import java.util.stream.Collectors; - -import static my.sample.java8.contract.rockps.api.RockPaperScissorsContract.Keys.*; - -@ContractHandler -public class RockPaperScissorsContractImpl implements RockPaperScissorsContract { - - private final ContractState contractState; - private final ContractTransaction tx; - - private final Mapping players; - - public RockPaperScissorsContractImpl(ContractState contractState, ContractTransaction tx) { - this.tx = tx; - this.contractState = contractState; - this.players = contractState.getMapping(Player.class, PLAYERS_MAPPING_PREFIX); - } - - public void createGame(CreateGameRequest createGameRequest) { - if (createGameRequest.getPlayers().size() > 2) { - throw new IllegalArgumentException("Currently only two players are supported"); - } - contractState.put(CREATED_DATE_KEY, txTimestamp().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)); - contractState.put( - PLAYER_ADDRESSES_KEY, - createGameRequest.getPlayers().stream().map(CreateGameRequest.Player::getAddress).collect(Collectors.toSet()) - ); - } - - public void play(PlayRequest registerPlayerRequest) { - if (contractState.tryGet(GAME_KEY, Game.class).isPresent()) { - throw new IllegalStateException("Game has already started"); - } - String senderAddress = senderAddress(); - Set addresses = getPlayerAddresses(); - String txSenderAddress = txSender(); - if (!addresses.contains(txSenderAddress)) { - throw new IllegalAccessError("Address " + txSenderAddress + " is not present in the players of this game"); - } - players.put(senderAddress(), new Player(senderAddress, registerPlayerRequest.getHashedAnswer())); - if (players.hasAll(addresses)) { - startGame(addresses); - } - } - - public void reveal(RevealRequest revealRequest) { - Game game = contractState.tryGet(GAME_KEY, Game.class) - .orElseThrow(() -> new IllegalStateException("Could not reveal my result for not active game")); - if (game.getStatus() == GameStatus.FINISHED) { - throw new IllegalStateException("Game has already finished"); - } - String currentAddress = txSender(); - game.reveal(currentAddress, revealRequest.getSalt()); - players.put(currentAddress, game.getPlayers().get(currentAddress)); - contractState.put(GAME_KEY, game); - contractState.put(GAME_STATUS_KEY, game.getStatus()); - if (game.getWinner() != null) { - contractState.put(GAME_WINNER_ADDR_KEY, game.getWinner().getAddress()); - } - } - - private void startGame(Set addresses) { - Game game = new Game(new ArrayList<>(players.getAll(addresses).values())); - contractState.put(GAME_KEY, game); - contractState.put(GAME_STATUS_KEY, game.getStatus()); - } - - private Set getPlayerAddresses() { - return contractState.get(PLAYER_ADDRESSES_KEY, new TypeReference>() { - }); - } - - @NotNull - private String txSender() { - return tx.getSender().asBase58String(); - } - - private OffsetDateTime txTimestamp() { - return OffsetDateTime.ofInstant( - Instant.ofEpochMilli(tx.getTimestamp().getUtcTimestampMillis()), - ZoneId.of("UTC") - ); - } - - private String senderAddress() { - return txSender(); - } -} diff --git a/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/api/RockPaperScissorsContract.java b/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/api/RockPaperScissorsContract.java deleted file mode 100644 index 4f1579c..0000000 --- a/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/api/RockPaperScissorsContract.java +++ /dev/null @@ -1,30 +0,0 @@ -package my.sample.java8.contract.rockps.api; - -import com.wavesenterprise.sdk.contract.api.annotation.ContractAction; -import com.wavesenterprise.sdk.contract.api.annotation.ContractInit; -import my.sample.java8.contract.rockps.domain.game.request.CreateGameRequest; -import my.sample.java8.contract.rockps.domain.game.request.PlayRequest; -import my.sample.java8.contract.rockps.domain.game.request.RevealRequest; - -public interface RockPaperScissorsContract { - - @ContractInit - void createGame(CreateGameRequest createGameRequest); - - @ContractAction - void play(PlayRequest registerPlayerRequest); - - @ContractAction - void reveal(RevealRequest revealRequest); - - class Keys { - public static final String PLAYER_ADDRESSES_KEY = "PLAYER_ADDRESSES"; - public static final String PLAYERS_MAPPING_PREFIX = "PLAYERS"; - - public final static String GAME_KEY = "GAME"; - public static final String GAME_STATUS_KEY = "GAME_STATUS"; - public static final String GAME_WINNER_ADDR_KEY = "GAME_WINNER_ADDRESS"; - - public static final String CREATED_DATE_KEY = "CREATED_DATE"; - } -} diff --git a/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/domain/game/AnswerType.java b/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/domain/game/AnswerType.java deleted file mode 100644 index 1a0363a..0000000 --- a/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/domain/game/AnswerType.java +++ /dev/null @@ -1,7 +0,0 @@ -package my.sample.java8.contract.rockps.domain.game; - -public enum AnswerType { - ROCK, - PAPER, - SCISSORS; -} diff --git a/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/domain/game/Game.java b/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/domain/game/Game.java deleted file mode 100644 index b377fce..0000000 --- a/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/domain/game/Game.java +++ /dev/null @@ -1,85 +0,0 @@ -package my.sample.java8.contract.rockps.domain.game; - -import my.sample.java8.contract.rockps.util.Util; - -import java.time.OffsetDateTime; -import java.util.*; -import java.util.function.Function; -import java.util.stream.Collectors; - -public class Game { - private Map players; - private Player winner; - private GameStatus status = GameStatus.ACTIVE; - - public Game() { - } - - public Map getPlayers() { - return players; - } - - public Player getWinner() { - return winner; - } - - public Game(List players) { - this.players = players.stream().collect(Collectors.toMap(Player::getAddress, Function.identity())); - } - - public void reveal(String address, String salt) { - Player player = players.get(address); - if (player == null) { - throw new IllegalArgumentException("Address " + address + " isn't among players of the game"); - } - Map hashedAnswers = getHashedAnswers(salt); - AnswerType answer = hashedAnswers.get(player.getHashedAnswer()); - if (answer == null) { - throw new IllegalArgumentException("Not found matching answer for salt"); - } - player.setAnswer(answer); - if (allPlayersAnswered()) { - finish(); - } - } - - private void finish() { - status = GameStatus.FINISHED; - if (allPlayersHaveTheSameAnswers()) { - return; - } - this.winner = players.values().stream().max( - (player1, player2) -> new AnswerComparator().compare(player1.getAnswer(), player2.getAnswer()) - ).orElseThrow(() -> new IllegalStateException("Winner could not be determined")); - } - - private boolean allPlayersHaveTheSameAnswers() { - Set differentAnswers = players.values().stream().map(Player::getAnswer).collect(Collectors.toSet()); - return differentAnswers.size() == 1; - } - - private boolean allPlayersAnswered() { - return players.values().stream().allMatch(player -> player.getAnswer() != null); - } - - public Map getHashedAnswers(String salt) { - return Arrays.stream(AnswerType.values()).collect( - Collectors.toMap(answerType -> Util.hash(answerType + "_" + salt), Function.identity()) - ); - } - - public GameStatus getStatus() { - return status; - } - - static class AnswerComparator implements Comparator { - - @Override - public int compare(AnswerType o1, AnswerType o2) { - if ((o1 == AnswerType.SCISSORS && o2 == AnswerType.ROCK) || (o1 == AnswerType.ROCK && o2 == AnswerType.SCISSORS)) { - return -o1.compareTo(o2); - } - return o1.compareTo(o2); - } - } -} \ No newline at end of file diff --git a/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/domain/game/GameStatus.java b/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/domain/game/GameStatus.java deleted file mode 100644 index 9ccfc35..0000000 --- a/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/domain/game/GameStatus.java +++ /dev/null @@ -1,5 +0,0 @@ -package my.sample.java8.contract.rockps.domain.game; - -public enum GameStatus { - ACTIVE, FINISHED -} diff --git a/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/domain/game/Player.java b/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/domain/game/Player.java deleted file mode 100644 index c62ad41..0000000 --- a/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/domain/game/Player.java +++ /dev/null @@ -1,32 +0,0 @@ -package my.sample.java8.contract.rockps.domain.game; - -public class Player { - private String address; - private String hashedAnswer; - - public Player() { - } - - private AnswerType answer; - - public Player(String address, String hashedAnswer) { - this.address = address; - this.hashedAnswer = hashedAnswer; - } - - public String getAddress() { - return address; - } - - public String getHashedAnswer() { - return hashedAnswer; - } - - public AnswerType getAnswer() { - return answer; - } - - public void setAnswer(AnswerType answer) { - this.answer = answer; - } -} diff --git a/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/domain/game/request/CreateGameRequest.java b/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/domain/game/request/CreateGameRequest.java deleted file mode 100644 index 3de66bb..0000000 --- a/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/domain/game/request/CreateGameRequest.java +++ /dev/null @@ -1,41 +0,0 @@ -package my.sample.java8.contract.rockps.domain.game.request; - -import java.util.List; - -public class CreateGameRequest { - private List players; - - public CreateGameRequest() { - } - - public CreateGameRequest(List players) { - this.players = players; - } - - public void setPlayers(List players) { - this.players = players; - } - - public List getPlayers() { - return players; - } - - public static class Player { - private String address; - - public Player() { - } - - public Player(String address) { - this.address = address; - } - - public void setAddress(String address) { - this.address = address; - } - - public String getAddress() { - return address; - } - } -} diff --git a/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/domain/game/request/PlayRequest.java b/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/domain/game/request/PlayRequest.java deleted file mode 100644 index ed72635..0000000 --- a/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/domain/game/request/PlayRequest.java +++ /dev/null @@ -1,20 +0,0 @@ -package my.sample.java8.contract.rockps.domain.game.request; - -public class PlayRequest { - private String hashedAnswer; - - public PlayRequest() { - } - - public PlayRequest(String hashedAnswer) { - this.hashedAnswer = hashedAnswer; - } - - public void setHashedAnswer(String hashedAnswer) { - this.hashedAnswer = hashedAnswer; - } - - public String getHashedAnswer() { - return hashedAnswer; - } -} diff --git a/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/domain/game/request/RevealRequest.java b/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/domain/game/request/RevealRequest.java deleted file mode 100644 index c020392..0000000 --- a/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/domain/game/request/RevealRequest.java +++ /dev/null @@ -1,20 +0,0 @@ -package my.sample.java8.contract.rockps.domain.game.request; - -public class RevealRequest { - private String salt; - - public RevealRequest() { - } - - public RevealRequest(String salt) { - this.salt = salt; - } - - public void setSalt(String salt) { - this.salt = salt; - } - - public String getSalt() { - return salt; - } -} diff --git a/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/util/Util.java b/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/util/Util.java deleted file mode 100644 index 9f7f241..0000000 --- a/samples/java8-sample-contract/src/main/java/my/sample/java8/contract/rockps/util/Util.java +++ /dev/null @@ -1,32 +0,0 @@ -package my.sample.java8.contract.rockps.util; - -import org.apache.commons.codec.digest.DigestUtils; - -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -public class Util { - - private Util() { - } - - public static String hash(String value) { - final MessageDigest digest = DigestUtils.getSha256Digest(); - final byte[] hashBytes = digest.digest( - value.getBytes(StandardCharsets.UTF_8)); - return bytesToHex(hashBytes); - } - - private static String bytesToHex(byte[] hash) { - StringBuilder hexString = new StringBuilder(2 * hash.length); - for (int i = 0; i < hash.length; i++) { - String hex = Integer.toHexString(0xff & hash[i]); - if (hex.length() == 1) { - hexString.append('0'); - } - hexString.append(hex); - } - return hexString.toString(); - } -} diff --git a/samples/java8-sample-contract/src/test/java/my/sample/java8/contract/rockps/RockPaperScissorsContractTest.java b/samples/java8-sample-contract/src/test/java/my/sample/java8/contract/rockps/RockPaperScissorsContractTest.java deleted file mode 100644 index e04fce9..0000000 --- a/samples/java8-sample-contract/src/test/java/my/sample/java8/contract/rockps/RockPaperScissorsContractTest.java +++ /dev/null @@ -1,209 +0,0 @@ -package my.sample.java8.contract.rockps; - -import com.fasterxml.jackson.databind.json.JsonMapper; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import com.wavesenterprise.sdk.contract.api.state.ContractState; -import com.wavesenterprise.sdk.contract.api.state.TypeReference; -import com.wavesenterprise.sdk.contract.api.state.mapping.Mapping; -import com.wavesenterprise.sdk.contract.test.state.ContractTestStateFactory; -import com.wavesenterprise.sdk.node.domain.Address; -import com.wavesenterprise.sdk.node.domain.TxType; -import my.sample.java8.contract.rockps.api.RockPaperScissorsContract; -import my.sample.java8.contract.rockps.domain.game.AnswerType; -import my.sample.java8.contract.rockps.domain.game.Game; -import my.sample.java8.contract.rockps.domain.game.GameStatus; -import my.sample.java8.contract.rockps.domain.game.Player; -import my.sample.java8.contract.rockps.domain.game.request.CreateGameRequest; -import my.sample.java8.contract.rockps.domain.game.request.PlayRequest; -import my.sample.java8.contract.rockps.domain.game.request.RevealRequest; -import my.sample.java8.contract.rockps.util.Util; -import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.Test; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import static com.wavesenterprise.sdk.contract.test.data.TestDataProvider.address; -import static com.wavesenterprise.sdk.contract.test.data.TestDataProvider.contractTransaction; -import static my.sample.java8.contract.rockps.api.RockPaperScissorsContract.Keys.*; -import static org.junit.jupiter.api.Assertions.*; - -class RockPaperScissorsContractTest { - - JsonMapper objectMapper = JsonMapper.builder().addModule(new JavaTimeModule()).build(); - - @Test - public void shouldThrowErrorWhenMoreThanTwoPlayers() { - Address player1 = address(); - Address player2 = address(); - Address player3 = address(); - List
players = Arrays.asList(player1, player2, player3); - ContractState testState = ContractTestStateFactory.state(objectMapper); - RockPaperScissorsContractImpl rockPaperScissorsContract = new RockPaperScissorsContractImpl( - testState, - contractTransaction(address(), TxType.CREATE_CONTRACT) - ); - - assertThrows(IllegalArgumentException.class, () -> rockPaperScissorsContract.createGame( - gameRequest(players) - )); - } - - @Test - public void shouldCreateGame() { - Address player1 = address(); - Address player2 = address(); - List
players = Arrays.asList(player1, player2); - ContractState testState = ContractTestStateFactory.state(objectMapper); - RockPaperScissorsContractImpl rockPaperScissorsContract = new RockPaperScissorsContractImpl( - testState, - contractTransaction(address(), TxType.CREATE_CONTRACT) - ); - - rockPaperScissorsContract.createGame( - gameRequest(players) - ); - - Set playerAddresses = testState.get(PLAYER_ADDRESSES_KEY, new TypeReference>() { - }); - assertEquals(players.size(), playerAddresses.size()); - assertFalse(testState.tryGet(GAME_KEY, Game.class).isPresent()); - } - - @Test - public void shouldThrowErrorWhenPlayingByNotKnownAddress() { - Address player1 = address(); - Address player2 = address(); - Address sender = address(); - List
players = Arrays.asList(player1, player2); - ContractState testState = ContractTestStateFactory.state(objectMapper); - RockPaperScissorsContractImpl rockPaperScissorsContract = new RockPaperScissorsContractImpl( - testState, - contractTransaction(sender, TxType.CALL_CONTRACT) - ); - - rockPaperScissorsContract.createGame( - gameRequest(players) - ); - assertThrows( - IllegalAccessError.class, - () -> rockPaperScissorsContract.play(new PlayRequest("someHash")) - ); - } - - @NotNull - private CreateGameRequest gameRequest(List
players) { - return new CreateGameRequest( - players.stream().map( - address -> new CreateGameRequest.Player(address.asBase58String()) - ).collect(Collectors.toList()) - ); - } - - @Test - public void shouldPlay() { - Address player1 = address(); - Address player2 = address(); - Address sender = address(); - List
players = Arrays.asList(player1, player2); - ContractState testState = ContractTestStateFactory.state(objectMapper); - RockPaperScissorsContractImpl rockPaperScissorsContract = new RockPaperScissorsContractImpl( - testState, - contractTransaction(sender, TxType.CALL_CONTRACT) - ); - - rockPaperScissorsContract.createGame( - gameRequest(players) - ); - assertThrows( - IllegalAccessError.class, - () -> rockPaperScissorsContract.play(new PlayRequest("someHash")) - ); - } - - @Test - public void playGame() { - Address currentAddress = address(); - ContractState testState = ContractTestStateFactory.state(objectMapper); - Mapping players = testState.getMapping(Player.class, PLAYERS_MAPPING_PREFIX); - RockPaperScissorsContractImpl rockPaperScissorsContract = new RockPaperScissorsContractImpl( - testState, - contractTransaction(currentAddress, TxType.CREATE_CONTRACT) - ); - String currentAddressStr = currentAddress.asBase58String(); - testState.put(PLAYER_ADDRESSES_KEY, Collections.singletonList(currentAddressStr)); - String hashedAnswer = "hashed"; - - rockPaperScissorsContract.play(new PlayRequest(hashedAnswer)); - - Player player = players.get(currentAddressStr); - assertEquals(hashedAnswer, player.getHashedAnswer()); - } - - @Test - public void shouldRevealResult() { - Address currentAddress = address(); - ContractState testState = ContractTestStateFactory.state(objectMapper); - Mapping players = testState.getMapping(Player.class, PLAYERS_MAPPING_PREFIX); - RockPaperScissorsContractImpl rockPaperScissorsContract = new RockPaperScissorsContractImpl( - testState, - contractTransaction(currentAddress, TxType.CREATE_CONTRACT) - ); - String currentAddressStr = currentAddress.asBase58String(); - testState.put(PLAYER_ADDRESSES_KEY, Collections.singletonList(currentAddressStr)); - String salt = "asdfb23"; - AnswerType answerType = AnswerType.PAPER; - String hashedAnswer = Util.hash(answerType + "_" + salt); - rockPaperScissorsContract.play(new PlayRequest(hashedAnswer)); - - rockPaperScissorsContract.reveal(new RevealRequest(salt)); - - Player player = players.get(currentAddressStr); - assertEquals(player.getAnswer(), AnswerType.PAPER); - } - - @Test - public void shouldPassIntegrationTest() { - Address player1 = address(); - String player1Salt = "salt1234"; - AnswerType player1AnswerType = AnswerType.PAPER; - Address player2 = address(); - String player2Salt = "salt567"; - AnswerType player2AnswerType = AnswerType.SCISSORS; - Address contractCreator = address(); - ContractState testState = ContractTestStateFactory.state(objectMapper); - - RockPaperScissorsContract rockPaperScissorsContract = new RockPaperScissorsContractImpl( - testState, - contractTransaction(contractCreator, TxType.CREATE_CONTRACT) - ); - rockPaperScissorsContract.createGame( - gameRequest(Arrays.asList(player1, player2)) - ); - - RockPaperScissorsContract rockPaperScissorsContractForPlayer1 = new RockPaperScissorsContractImpl( - testState, - contractTransaction(player1, TxType.CALL_CONTRACT) - ); - rockPaperScissorsContractForPlayer1.play(new PlayRequest(Util.hash(player1AnswerType + "_" + player1Salt))); - - - RockPaperScissorsContract rockPaperScissorsContractForPlayer2 = new RockPaperScissorsContractImpl( - testState, - contractTransaction(player2, TxType.CALL_CONTRACT) - ); - rockPaperScissorsContractForPlayer2.play(new PlayRequest(Util.hash(player2AnswerType + "_" + player2Salt))); - - rockPaperScissorsContractForPlayer1.reveal(new RevealRequest(player1Salt)); - rockPaperScissorsContractForPlayer2.reveal(new RevealRequest(player2Salt)); - - GameStatus gameStatus = testState.get(GAME_STATUS_KEY, GameStatus.class); - assertEquals(GameStatus.FINISHED, gameStatus); - String winnerAddress = testState.get(GAME_WINNER_ADDR_KEY, String.class); - assertEquals(player2.asBase58String(), winnerAddress); - } - -} \ No newline at end of file diff --git a/samples/java8-sample-contract/src/test/java/my/sample/java8/contract/rockps/domain/game/AnswerComparatorTest.java b/samples/java8-sample-contract/src/test/java/my/sample/java8/contract/rockps/domain/game/AnswerComparatorTest.java deleted file mode 100644 index 72de55a..0000000 --- a/samples/java8-sample-contract/src/test/java/my/sample/java8/contract/rockps/domain/game/AnswerComparatorTest.java +++ /dev/null @@ -1,35 +0,0 @@ -package my.sample.java8.contract.rockps.domain.game; - -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -import java.util.stream.Stream; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -class AnswerComparatorTest { - - @ParameterizedTest - @MethodSource("values") - public void testAnswerComparator(AnswerType answer1, AnswerType answer2, int result) { - Game.AnswerComparator comparator = new Game.AnswerComparator(); - assertEquals(result, comparator.compare(answer1, answer2)); - } - - private static Stream values() { - return Stream.of( - Arguments.of(AnswerType.PAPER, AnswerType.PAPER, 0), - Arguments.of(AnswerType.PAPER, AnswerType.ROCK, 1), - Arguments.of(AnswerType.ROCK, AnswerType.PAPER, -1), - Arguments.of(AnswerType.ROCK, AnswerType.ROCK, 0), - Arguments.of(AnswerType.SCISSORS, AnswerType.ROCK, -2), - Arguments.of(AnswerType.ROCK, AnswerType.SCISSORS, 2), - Arguments.of(AnswerType.SCISSORS, AnswerType.SCISSORS, 0), - Arguments.of(AnswerType.SCISSORS, AnswerType.PAPER, 1), - Arguments.of(AnswerType.PAPER, AnswerType.SCISSORS, -1), - Arguments.of(AnswerType.PAPER, AnswerType.PAPER, 0) - ); - } - -} \ No newline at end of file diff --git a/samples/kotlin-sample/kotlin-backend-app/build.gradle.kts b/samples/kotlin-sample/kotlin-backend-app/build.gradle.kts index ee0a6d3..c696ea8 100644 --- a/samples/kotlin-sample/kotlin-backend-app/build.gradle.kts +++ b/samples/kotlin-sample/kotlin-backend-app/build.gradle.kts @@ -60,13 +60,13 @@ tasks.getByName("test") { tasks.withType().configureEach { kotlinOptions { freeCompilerArgs = listOf("-Xjsr305=strict") - jvmTarget = JavaVersion.VERSION_11.toString() + jvmTarget = JavaVersion.VERSION_17.toString() } } java { toolchain { - languageVersion.set(JavaLanguageVersion.of(11)) + languageVersion.set(JavaLanguageVersion.of(17)) } } diff --git a/samples/kotlin-sample/kotlin-contract/build.gradle.kts b/samples/kotlin-sample/kotlin-contract/build.gradle.kts index bbc4c39..ff21c34 100644 --- a/samples/kotlin-sample/kotlin-contract/build.gradle.kts +++ b/samples/kotlin-sample/kotlin-contract/build.gradle.kts @@ -48,13 +48,13 @@ tasks.getByName("test") { tasks.withType().configureEach { kotlinOptions { freeCompilerArgs = listOf("-Xjsr305=strict") - jvmTarget = JavaVersion.VERSION_11.toString() + jvmTarget = JavaVersion.VERSION_17.toString() } } java { toolchain { - languageVersion.set(JavaLanguageVersion.of(11)) + languageVersion.set(JavaLanguageVersion.of(17)) } } diff --git a/samples/settings.gradle.kts b/samples/settings.gradle.kts index 7438315..72a2928 100644 --- a/samples/settings.gradle.kts +++ b/samples/settings.gradle.kts @@ -2,8 +2,6 @@ rootProject.name = "we-contract-sdk-samples" include( "java17-sample-contract", - "java8-sample-contract", - "kotlin-sample:kotlin-contract", "kotlin-sample:kotlin-backend-app" ) diff --git a/samples/take_root_version_for_build.sh b/samples/take_root_version_for_build.sh index 4130362..9104c43 100755 --- a/samples/take_root_version_for_build.sh +++ b/samples/take_root_version_for_build.sh @@ -6,3 +6,5 @@ echo $version cd samples ./gradlew clean check -PweContractSdkVersion="$version" + +# TODO: https://jira.web3tech.ru/browse/WTCH-293 \ No newline at end of file diff --git a/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateExternalWithCacheTest.kt b/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateExternalWithCacheTest.kt index b0e3720..efb6999 100644 --- a/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateExternalWithCacheTest.kt +++ b/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateExternalWithCacheTest.kt @@ -3,7 +3,6 @@ package com.wavesenterprise.sdk.contract.core.state import com.wavesenterprise.sdk.contract.api.state.ContractState import com.wavesenterprise.sdk.contract.core.utils.contractId import com.wavesenterprise.sdk.node.domain.contract.ContractId -import io.mockk.Called import io.mockk.mockk import io.mockk.verify import org.junit.jupiter.api.Assertions.assertSame @@ -16,7 +15,7 @@ class ContractStateExternalWithCacheTest { private var cache = ContractStateWithCachedExternalContracts(delegate, externalStates) @Test - fun `test`() { + fun `should use cache state if exist`() { val contractState = mockk() val contractIdInCache = contractId() val contractIdNotInCache = contractId() @@ -26,6 +25,6 @@ class ContractStateExternalWithCacheTest { assertSame(cache.external(contractIdInCache), contractState) verify { delegate.external(contractIdNotInCache) } - verify { delegate.external(contractIdInCache) wasNot Called } + verify(exactly = 0) { delegate.external(contractIdInCache) } } } diff --git a/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateTest.kt b/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateTest.kt index 0f75d8d..eaf2d6d 100644 --- a/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateTest.kt +++ b/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateTest.kt @@ -40,7 +40,7 @@ internal class ContractStateTest { @MockK lateinit var contractToDataValueConverter: ContractToDataValueConverter - private val mappingMapForState: MutableMap> = spyk(hashMapOf()) + private val mappingMapForState: MutableMap> = spyk() private lateinit var contractState: ContractState From f5d6a2c47a4499ba96dd531b238a3db56b78d857 Mon Sep 17 00:00:00 2001 From: dgeorgiev Date: Thu, 27 Jun 2024 14:30:13 +0300 Subject: [PATCH 02/20] [WTCH-303] Upgrade kotlin to 1.9.23, detekt to 1.23.6, mockk to 1.13.11 --- gradle.properties | 6 +- gradle/detekt-config.yml | 126 ++---------------- .../contract/api/state/ContractStateReader.kt | 8 +- .../contract/api/state/ContractStateWriter.kt | 4 +- .../sdk/contract/api/state/mapping/Mapping.kt | 2 +- .../contract/api/state/mapping/ReadMapping.kt | 2 +- .../contract/core/state/ContractStateEx.kt | 2 +- .../contract/core/state/ContractStateImpl.kt | 4 +- .../core/state/ContractStateReaderIml.kt | 8 +- .../core/state/mapping/MappingImpl.kt | 2 +- .../core/state/mapping/ReadMappingForType.kt | 2 +- .../mapping/ReadMappingForTypeReference.kt | 2 +- 12 files changed, 34 insertions(+), 134 deletions(-) diff --git a/gradle.properties b/gradle.properties index 07c1c2e..c3e2072 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,7 +7,7 @@ org.gradle.jvmargs=-Xmx4g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 # Build plugins gradleDependencyManagementVersion=1.0.8.RELEASE -detektVersion=1.19.0 +detektVersion=1.23.6 ktlintVersion=10.2.1 gitPropertiesVersion=2.2.2 palantirGitVersion=0.12.2 @@ -17,7 +17,7 @@ dokkaVersion=1.6.21 nexusStagingVersion=0.30.0 # Core infrastructure libs versions -kotlinVersion=1.6.21 +kotlinVersion=1.9.23 kotlinCoroutinesVersion=1.6.1 logbackVersion=1.2.11 sl4jExtVersion=2.0.0 @@ -31,7 +31,7 @@ ioGrpcVersion=1.20.0 # Testing junitVersion=5.8.2 -mockkVersion=1.12.3 +mockkVersion=1.13.11 springMockkVersion=3.1.1 # WE SDK diff --git a/gradle/detekt-config.yml b/gradle/detekt-config.yml index 50bad5a..037f7b8 100644 --- a/gradle/detekt-config.yml +++ b/gradle/detekt-config.yml @@ -57,7 +57,7 @@ complexity: active: false threshold: 10 includeStaticDeclarations: false - ComplexMethod: + CyclomaticComplexMethod: active: true threshold: 10 ignoreSingleWhenExpression: false @@ -214,99 +214,6 @@ exceptions: - Throwable - RuntimeException -formatting: - active: true - android: false - autoCorrect: true - ChainWrapping: - active: true - autoCorrect: true - CommentSpacing: - active: true - autoCorrect: true - Filename: - active: true - FinalNewline: - active: true - autoCorrect: true - ImportOrdering: - active: false - Indentation: - active: true - autoCorrect: true - indentSize: 4 - continuationIndentSize: 4 - MaximumLineLength: - active: true - maxLineLength: 120 - ModifierOrdering: - active: true - autoCorrect: true - NoBlankLineBeforeRbrace: - active: true - autoCorrect: true - NoConsecutiveBlankLines: - active: true - autoCorrect: true - NoEmptyClassBody: - active: true - autoCorrect: true - NoLineBreakAfterElse: - active: true - autoCorrect: true - NoLineBreakBeforeAssignment: - active: true - autoCorrect: true - NoMultipleSpaces: - active: true - autoCorrect: true - NoSemicolons: - active: true - autoCorrect: true - NoTrailingSpaces: - active: true - autoCorrect: true - NoUnitReturn: - active: true - autoCorrect: true - NoUnusedImports: - active: true - autoCorrect: true - NoWildcardImports: - active: true - autoCorrect: true - PackageName: - active: true - autoCorrect: true - ParameterListWrapping: - active: true - autoCorrect: true - indentSize: 4 - SpacingAroundColon: - active: true - autoCorrect: true - SpacingAroundComma: - active: true - autoCorrect: true - SpacingAroundCurly: - active: true - autoCorrect: true - SpacingAroundKeyword: - active: true - autoCorrect: true - SpacingAroundOperators: - active: true - autoCorrect: true - SpacingAroundParens: - active: true - autoCorrect: true - SpacingAroundRangeOperator: - active: true - autoCorrect: true - StringTemplate: - active: true - autoCorrect: true - naming: active: true ClassNaming: @@ -375,7 +282,6 @@ naming: - "**/*.Spek.kt" functionPattern: '^([a-z$][a-zA-Z$0-9]*)|(`.*`)$' excludeClassPattern: '$^' - ignoreOverridden: true FunctionParameterNaming: active: true excludes: @@ -386,7 +292,6 @@ naming: - "**/*.Spek.kt" parameterPattern: '[a-z][A-Za-z0-9]*' excludeClassPattern: '$^' - ignoreOverridden: true InvalidPackageDeclaration: active: false rootPackage: '' @@ -455,7 +360,6 @@ naming: variablePattern: '[a-z][A-Za-z0-9]*' privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' excludeClassPattern: '$^' - ignoreOverridden: true performance: active: true @@ -482,8 +386,6 @@ performance: potential-bugs: active: true - DuplicateCaseInWhenExpression: - active: true EqualsAlwaysReturnsTrueOrFalse: active: false EqualsWithHashCodeExist: @@ -504,10 +406,8 @@ potential-bugs: - "**/*.Test.kt" - "**/*.Spec.kt" - "**/*.Spek.kt" - excludeAnnotatedProperties: + ignoreAnnotated: ignoreOnClassesPattern: "" - MissingWhenCase: - active: false UnconditionalJumpStatementInLoop: active: false UnreachableCode: @@ -527,7 +427,7 @@ style: active: false DataClassContainsFunctions: active: false - conversionFunctionPrefix: 'to' + conversionFunctionPrefix: [ 'to' ] DataClassShouldBeImmutable: active: false EqualsNullCall: @@ -540,8 +440,8 @@ style: active: false includeLineWrapping: false ForbiddenComment: - active: true - values: + active: false + comments : - TODO - FIXME - STOPSHIP @@ -554,7 +454,7 @@ style: FunctionOnlyReturningConstant: active: false ignoreOverridableFunction: true - excludedFunctions: 'describeContents' + excludedFunctions: ['describeContents' ] LoopWithTooManyJumpStatements: active: false maxJumpCount: 1 @@ -579,8 +479,6 @@ style: ignoreNamedArgument: true ignoreEnums: false ignoreRanges: false - MandatoryBracesIfStatements: - active: false MaxLineLength: active: true maxLineLength: 120 @@ -601,7 +499,7 @@ style: active: true OptionalUnit: active: false - OptionalWhenBraces: + BracesOnWhenStatements: active: false PreferToOverPairSyntax: active: false @@ -612,7 +510,7 @@ style: ReturnCount: active: true max: 3 - excludedFunctions: "equals" + excludedFunctions: [ "equals" ] excludeLabeled: false excludeReturnFromLambda: true SafeCast: @@ -628,10 +526,10 @@ style: active: false UnderscoresInNumericLiterals: active: false - acceptableDecimalLength: 5 + acceptableLength: 5 UnnecessaryAbstractClass: active: false - excludeAnnotatedClasses: + ignoreAnnotated: - dagger.Module UnnecessaryApply: active: false @@ -645,6 +543,8 @@ style: active: false UnusedImports: active: false + UnusedParameter: + active: false UnusedPrivateClass: active: false UnusedPrivateMember: @@ -654,7 +554,7 @@ style: active: false UseDataClass: active: false - excludeAnnotatedClasses: + ignoreAnnotated: UseRequire: active: false UselessCallOnNotNull: diff --git a/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/state/ContractStateReader.kt b/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/state/ContractStateReader.kt index bf7b38c..7d47f9c 100644 --- a/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/state/ContractStateReader.kt +++ b/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/state/ContractStateReader.kt @@ -21,7 +21,7 @@ interface ContractStateReader { * @param valueType type of returned value * @return Optional */ - fun tryGet(key: String, valueType: Class): Optional + fun tryGet(key: String, valueType: Class): Optional /** * Finds value by key @@ -39,7 +39,7 @@ interface ContractStateReader { * @param typeReference typeReference of returned value * @return result found or null wrapped by Optional */ - fun tryGet(key: String, typeReference: TypeReference): Optional + fun tryGet(key: String, typeReference: TypeReference): Optional /** * Finds Boolean value by key @@ -97,7 +97,7 @@ interface ContractStateReader { * @param prefix prefix of the mapping * @return ReadMapping for type */ - fun getMapping(type: Class, vararg prefix: String): ReadMapping + fun getMapping(type: Class, vararg prefix: String): ReadMapping /** * Constructs READ ONLY mapping for TypeReference and prefix @@ -105,7 +105,7 @@ interface ContractStateReader { * @param prefix prefix of the mapping * @return ReadMapping for typeReference */ - fun getMapping(typeReference: TypeReference, vararg prefix: String): ReadMapping + fun getMapping(typeReference: TypeReference, vararg prefix: String): ReadMapping /** * Get contract WRC12Meta if present diff --git a/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/state/ContractStateWriter.kt b/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/state/ContractStateWriter.kt index de2be41..4ba0cb9 100644 --- a/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/state/ContractStateWriter.kt +++ b/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/state/ContractStateWriter.kt @@ -22,7 +22,7 @@ interface ContractStateWriter { * @param type type of objects * @param prefix prefix parts joined by ContractKeys.PREFIX_KEY_DELIMITER */ - fun getMapping(type: Class, vararg prefix: String): Mapping + fun getMapping(type: Class, vararg prefix: String): Mapping /** * Constructs mapping of keys having values with specified type and prefix @@ -30,5 +30,5 @@ interface ContractStateWriter { * @param typeReference typeReference of objects * @param prefix prefix parts joined by ContractKeys.PREFIX_KEY_DELIMITER */ - fun getMapping(typeReference: TypeReference, vararg prefix: String): Mapping + fun getMapping(typeReference: TypeReference, vararg prefix: String): Mapping } diff --git a/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/state/mapping/Mapping.kt b/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/state/mapping/Mapping.kt index 5f19d7c..38e3219 100644 --- a/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/state/mapping/Mapping.kt +++ b/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/state/mapping/Mapping.kt @@ -1,3 +1,3 @@ package com.wavesenterprise.sdk.contract.api.state.mapping -interface Mapping : ReadMapping, WriteMapping +interface Mapping : ReadMapping, WriteMapping diff --git a/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/state/mapping/ReadMapping.kt b/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/state/mapping/ReadMapping.kt index 31b6948..e71f4f6 100644 --- a/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/state/mapping/ReadMapping.kt +++ b/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/state/mapping/ReadMapping.kt @@ -2,7 +2,7 @@ package com.wavesenterprise.sdk.contract.api.state.mapping import java.util.Optional -interface ReadMapping { +interface ReadMapping { /** * Method that find value by key with mapping diff --git a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateEx.kt b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateEx.kt index 3464550..f8e16ba 100644 --- a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateEx.kt +++ b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateEx.kt @@ -15,7 +15,7 @@ import kotlin.reflect.KProperty * An extension function that allows access to mapped value in * map-like fashion */ -inline operator fun Mapping.set(key: String, value: T) { +inline operator fun Mapping.set(key: String, value: T) { put(key, value) } diff --git a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateImpl.kt b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateImpl.kt index 71b7092..f75b0a7 100644 --- a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateImpl.kt +++ b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateImpl.kt @@ -43,7 +43,7 @@ class ContractStateImpl( override fun external(contractId: ContractId) = contractStateReaderFactory.buildContractState(contractId) - override fun getMapping(type: Class, vararg prefix: String): Mapping { + override fun getMapping(type: Class, vararg prefix: String): Mapping { val cacheKey = ClassMappingCacheKey(clazz = type, prefix = prefix.toList()) val result = mappingMap[cacheKey] return if (result != null) { @@ -61,7 +61,7 @@ class ContractStateImpl( } } - override fun getMapping(typeReference: TypeReference, vararg prefix: String): Mapping { + override fun getMapping(typeReference: TypeReference, vararg prefix: String): Mapping { val cacheKey = TypeMappingCacheKey(reference = typeReference, prefix = prefix.toList()) val result = mappingMap[cacheKey] return if (result != null) { diff --git a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateReaderIml.kt b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateReaderIml.kt index 710e33e..9cd026a 100644 --- a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateReaderIml.kt +++ b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateReaderIml.kt @@ -29,10 +29,10 @@ class ContractStateReaderIml( override fun get(key: String, typeReference: TypeReference): T = requireNotNull(getNullableValue(key, typeReference)) { errorMessage(key) } - override fun tryGet(key: String, valueType: Class): Optional = + override fun tryGet(key: String, valueType: Class): Optional = Optional.ofNullable(getNullableValue(key, valueType)) - override fun tryGet(key: String, typeReference: TypeReference): Optional = + override fun tryGet(key: String, typeReference: TypeReference): Optional = Optional.ofNullable(getNullableValue(key, typeReference)) override fun getBoolean(key: String): Boolean = @@ -59,14 +59,14 @@ class ContractStateReaderIml( } } - override fun getMapping(type: Class, vararg prefix: String): ReadMapping = + override fun getMapping(type: Class, vararg prefix: String): ReadMapping = ReadMappingForType( contractStateReader = this, keyMapper = PrefixedKeyMapper(*prefix), type = type ) - override fun getMapping(typeReference: TypeReference, vararg prefix: String): ReadMapping = + override fun getMapping(typeReference: TypeReference, vararg prefix: String): ReadMapping = ReadMappingForTypeReference( contractStateReader = this, keyMapper = PrefixedKeyMapper(*prefix), diff --git a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/mapping/MappingImpl.kt b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/mapping/MappingImpl.kt index c167f90..9fbb067 100644 --- a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/mapping/MappingImpl.kt +++ b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/mapping/MappingImpl.kt @@ -4,7 +4,7 @@ import com.wavesenterprise.sdk.contract.api.state.mapping.Mapping import com.wavesenterprise.sdk.contract.api.state.mapping.ReadMapping import com.wavesenterprise.sdk.contract.api.state.mapping.WriteMapping -class MappingImpl( +class MappingImpl( private val writeMapping: WriteMapping, private val readMapping: ReadMapping, ) : WriteMapping by writeMapping, diff --git a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/mapping/ReadMappingForType.kt b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/mapping/ReadMappingForType.kt index c7fae3f..8057530 100644 --- a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/mapping/ReadMappingForType.kt +++ b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/mapping/ReadMappingForType.kt @@ -5,7 +5,7 @@ import com.wavesenterprise.sdk.contract.api.state.mapping.KeyMapper import com.wavesenterprise.sdk.contract.api.state.mapping.ReadMapping import java.util.Optional -class ReadMappingForType( +class ReadMappingForType( private val contractStateReader: ContractStateReader, private val keyMapper: KeyMapper, private val type: Class, diff --git a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/mapping/ReadMappingForTypeReference.kt b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/mapping/ReadMappingForTypeReference.kt index 24a1c65..301c08e 100644 --- a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/mapping/ReadMappingForTypeReference.kt +++ b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/mapping/ReadMappingForTypeReference.kt @@ -6,7 +6,7 @@ import com.wavesenterprise.sdk.contract.api.state.mapping.KeyMapper import com.wavesenterprise.sdk.contract.api.state.mapping.ReadMapping import java.util.Optional -class ReadMappingForTypeReference( +class ReadMappingForTypeReference( private val contractStateReader: ContractStateReader, private val keyMapper: KeyMapper, private val typeReference: TypeReference, From 2c6f18e18536d79dbde7fd53316e071ff0aa6626 Mon Sep 17 00:00:00 2001 From: dgeorgiev Date: Fri, 28 Jun 2024 11:16:43 +0300 Subject: [PATCH 03/20] [WTCH-304] Remove kotlinx-coroutines-bom as not used --- build.gradle.kts | 2 -- gradle.properties | 1 - 2 files changed, 3 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 6aeeeb2..fa79ec5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,7 +5,6 @@ import org.gradle.api.tasks.testing.logging.TestLogEvent import org.jetbrains.kotlin.gradle.tasks.KotlinCompile val kotlinVersion: String by project -val kotlinCoroutinesVersion: String by project val reactorVersion: String by project val springCloudVersion: String by project val jacocoToolVersion: String by project @@ -263,7 +262,6 @@ configure( mavenBom("com.wavesenterprise:we-node-client-bom:$weNodeClientVersion") mavenBom("org.jetbrains.kotlin:kotlin-bom:$kotlinVersion") - mavenBom("org.jetbrains.kotlinx:kotlinx-coroutines-bom:$kotlinCoroutinesVersion") mavenBom("com.fasterxml.jackson:jackson-bom:$jacksonVersion") mavenBom("io.grpc:grpc-bom:$ioGrpcVersion") diff --git a/gradle.properties b/gradle.properties index c3e2072..8156c84 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,7 +18,6 @@ nexusStagingVersion=0.30.0 # Core infrastructure libs versions kotlinVersion=1.9.23 -kotlinCoroutinesVersion=1.6.1 logbackVersion=1.2.11 sl4jExtVersion=2.0.0 javaxAnnotationApiVersion=1.3.2 From 087cc3200d3ce0e48fe43cf6380c46338eac1684 Mon Sep 17 00:00:00 2001 From: dgeorgiev Date: Fri, 28 Jun 2024 11:48:15 +0300 Subject: [PATCH 04/20] [WTCH-305] Upgrade gradle to 8.8 --- gradle/wrapper/gradle-wrapper.jar | Bin 59536 -> 61624 bytes gradle/wrapper/gradle-wrapper.properties | 3 +- gradlew | 269 ++++++++++++++--------- gradlew.bat | 15 +- 4 files changed, 175 insertions(+), 112 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180f2ae8848c63b8b4dea2cb829da983f2fa..afba109285af78dbd2a1d187e33ac4f87c76e392 100644 GIT binary patch delta 36947 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

h3Y5Ua5>0<>`e@0qkQ+|8VA&>!KrR0;1 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 GO8pnEgtf;2 delta 35088 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 \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MSYS* | MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -106,80 +140,105 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 107acd3..93e3f59 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal From 1b4072d847f3d727a5722f73cf78c34953e53452 Mon Sep 17 00:00:00 2001 From: dgeorgiev Date: Fri, 28 Jun 2024 12:38:05 +0300 Subject: [PATCH 05/20] [WTCH-306] Upgrade io.spring.dependency-management to 1.1.5 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 8156c84..6523ad5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ org.gradle.parallel=true org.gradle.jvmargs=-Xmx4g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 # Build plugins -gradleDependencyManagementVersion=1.0.8.RELEASE +gradleDependencyManagementVersion=1.1.5 detektVersion=1.23.6 ktlintVersion=10.2.1 gitPropertiesVersion=2.2.2 From 825e892c78a1bfcf6c82a51614cf008710c85504 Mon Sep 17 00:00:00 2001 From: dgeorgiev Date: Fri, 28 Jun 2024 12:52:54 +0300 Subject: [PATCH 06/20] [WTCH-307] Remove springMockk dependency as not used --- build.gradle.kts | 2 -- gradle.properties | 1 - 2 files changed, 3 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index fa79ec5..0fcdfa7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -21,7 +21,6 @@ val protobufVersion: String by project val junitVersion: String by project val mockkVersion: String by project -val springMockkVersion: String by project val ktorVersion: String by project @@ -275,7 +274,6 @@ configure( dependency("org.apache.commons:commons-lang3:$commonsLangVersion") dependency("io.mockk:mockk:$mockkVersion") - dependency("com.ninja-squad:springmockk:$springMockkVersion") dependency("com.frimastudio:slf4j-kotlin-extensions:$sl4jExtVersion") } } diff --git a/gradle.properties b/gradle.properties index 6523ad5..18c1a8e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -31,7 +31,6 @@ ioGrpcVersion=1.20.0 # Testing junitVersion=5.8.2 mockkVersion=1.13.11 -springMockkVersion=3.1.1 # WE SDK weNodeClientVersion=1.4.1 From 1afb717335acfb208a1130c98582bca643e5cf38 Mon Sep 17 00:00:00 2001 From: dgeorgiev Date: Fri, 28 Jun 2024 13:00:39 +0300 Subject: [PATCH 07/20] [WTCH-310] Upgrade jackson-bom to 2.17.1 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 18c1a8e..b32c4b2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,7 +22,7 @@ logbackVersion=1.2.11 sl4jExtVersion=2.0.0 javaxAnnotationApiVersion=1.3.2 commonsLangVersion=3.12.0 -jacksonVersion=2.13.2 +jacksonVersion=2.17.1 guavaVersion=31.1-jre # Grpc From 6ad46c3e7bfc0c400b53810dc1ba2eb8d636764f Mon Sep 17 00:00:00 2001 From: dgeorgiev Date: Fri, 28 Jun 2024 13:06:10 +0300 Subject: [PATCH 08/20] [WTCH-300] Remove property ktorVersion as not used --- build.gradle.kts | 2 -- 1 file changed, 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 0fcdfa7..1b2d063 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -22,8 +22,6 @@ val protobufVersion: String by project val junitVersion: String by project val mockkVersion: String by project -val ktorVersion: String by project - val weNodeClientVersion: String by project val weMavenUser: String? by project From f59da73e280994615efb2e2cfd14f82a14485274 Mon Sep 17 00:00:00 2001 From: dgeorgiev Date: Fri, 28 Jun 2024 13:28:56 +0300 Subject: [PATCH 09/20] [WTCH-301] Upgrade grpc-bom to 1.64.0 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index b32c4b2..2fecf75 100644 --- a/gradle.properties +++ b/gradle.properties @@ -26,7 +26,7 @@ jacksonVersion=2.17.1 guavaVersion=31.1-jre # Grpc -ioGrpcVersion=1.20.0 +ioGrpcVersion=1.64.0 # Testing junitVersion=5.8.2 From 869d536c17d8db2fe0bdf390e009f5deb030f466 Mon Sep 17 00:00:00 2001 From: dgeorgiev Date: Fri, 28 Jun 2024 13:36:04 +0300 Subject: [PATCH 10/20] [WTCH-301] Remove property reactorVersion as not used --- build.gradle.kts | 1 - 1 file changed, 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 1b2d063..0c02526 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,7 +5,6 @@ import org.gradle.api.tasks.testing.logging.TestLogEvent import org.jetbrains.kotlin.gradle.tasks.KotlinCompile val kotlinVersion: String by project -val reactorVersion: String by project val springCloudVersion: String by project val jacocoToolVersion: String by project val logbackVersion: String by project From f73aafeeb7407381d4833b8cffeda44ab7b82117 Mon Sep 17 00:00:00 2001 From: dgeorgiev Date: Fri, 28 Jun 2024 13:58:07 +0300 Subject: [PATCH 11/20] [WTCH-301] Upgrade junit-bom to 5.10.2 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 2fecf75..856ee1a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -29,7 +29,7 @@ guavaVersion=31.1-jre ioGrpcVersion=1.64.0 # Testing -junitVersion=5.8.2 +junitVersion=5.10.2 mockkVersion=1.13.11 # WE SDK From bf1e2ff26c33a2ea6d1dc1317b29eda615a9824f Mon Sep 17 00:00:00 2001 From: dgeorgiev Date: Fri, 28 Jun 2024 14:28:18 +0300 Subject: [PATCH 12/20] [WTCH-319] Replace ktlint plugin with detekt-formatting and do format --- build.gradle.kts | 20 +- gradle.properties | 1 - gradle/detekt-config.yml | 982 +++++++++++++----- settings.gradle.kts | 2 - .../contract/api/annotation/ContractAction.kt | 2 +- .../contract/api/annotation/ContractInit.kt | 2 +- .../contract/api/annotation/InvokeParam.kt | 2 +- .../api/domain/ContractConcurrency.kt | 2 +- .../api/domain/DefaultContractCall.kt | 4 +- .../factory/ContractBlockingClientFactory.kt | 8 +- .../invocation/impl/ParamsBuilderImpl.kt | 4 +- .../core/dispatch/ApplierContractTxToState.kt | 4 +- .../core/dispatch/ContractDispatcher.kt | 14 +- .../core/dispatch/ContractTxToStateApplier.kt | 4 +- ...ngClientNodeContractStateValuesProvider.kt | 2 +- .../process/ContractHandlerFactoryImpl.kt | 6 +- .../ContractHandlerInvocationExtractor.kt | 10 +- .../contract/core/state/ContractStateEx.kt | 6 +- .../contract/core/state/ContractStateImpl.kt | 2 +- .../core/state/ContractStateReaderIml.kt | 8 +- .../state/SpecifiedContractStateProvider.kt | 2 +- .../core/state/mapping/PrefixedKeyMapper.kt | 2 +- .../core/state/mapping/WriteMappingImpl.kt | 4 +- .../GrpcJacksonContractDispatcherBuilder.kt | 16 +- .../connect/EnvGrpcConnectionProperties.kt | 2 +- .../grpc/connect/HeaderClientInterceptor.kt | 2 +- .../jackson/JacksonFromDataEntryConverter.kt | 2 +- .../contract/test/data/TestDataProvider.kt | 4 +- .../test/state/ContractTestStateFactory.kt | 2 +- 29 files changed, 805 insertions(+), 316 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 0c02526..e5f31d8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,6 +4,8 @@ import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestLogEvent import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +val detektVersion: String by project + val kotlinVersion: String by project val springCloudVersion: String by project val jacocoToolVersion: String by project @@ -42,8 +44,7 @@ plugins { signing id("io.codearte.nexus-staging") id("io.spring.dependency-management") apply false - id("io.gitlab.arturbosch.detekt") apply false - id("org.jlleitschuh.gradle.ktlint") apply false + id("io.gitlab.arturbosch.detekt") id("com.palantir.git-version") apply false id("com.gorylenko.gradle-git-properties") apply false id("fr.brouillard.oss.gradle.jgitver") @@ -138,11 +139,14 @@ configure( apply(plugin = "io.spring.dependency-management") apply(plugin = "kotlin") apply(plugin = "io.gitlab.arturbosch.detekt") - apply(plugin = "org.jlleitschuh.gradle.ktlint") apply(plugin = "jacoco") apply(plugin = "signing") apply(plugin = "org.jetbrains.dokka") + dependencies { + detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:$detektVersion") + } + val jacocoCoverageFile = "$buildDir/jacocoReports/test/jacocoTestReport.xml" tasks.withType { @@ -179,6 +183,16 @@ configure( buildUponDefaultConfig = true } + tasks.register("detektFormat") { + description = "Runs detekt with auto-correct to format the code." + group = "formatting" + autoCorrect = true + exclude("resources/") + exclude("build/") + config.setFrom(detektConfigFilePath) + setSource(files("src/main/java", "src/main/kotlin")) + } + val sourcesJar by tasks.creating(Jar::class) { group = JavaBasePlugin.DOCUMENTATION_GROUP description = "Assembles sources JAR" diff --git a/gradle.properties b/gradle.properties index 856ee1a..cb6518d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,7 +8,6 @@ org.gradle.jvmargs=-Xmx4g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 # Build plugins gradleDependencyManagementVersion=1.1.5 detektVersion=1.23.6 -ktlintVersion=10.2.1 gitPropertiesVersion=2.2.2 palantirGitVersion=0.12.2 jacocoToolVersion=0.8.8 diff --git a/gradle/detekt-config.yml b/gradle/detekt-config.yml index 037f7b8..870799d 100644 --- a/gradle/detekt-config.yml +++ b/gradle/detekt-config.yml @@ -1,55 +1,101 @@ build: - maxIssues: 100 + # TODO: https://jira.web3tech.ru/browse/WTCH-339 + maxIssues: 150 + excludeCorrectable: false weights: - # complexity: 2 - # LongParameterList: 1 - # style: 1 - # comments: 1 + # complexity: 2 + # LongParameterList: 1 + # style: 1 + # comments: 1 + +config: + validation: true + warningsAsErrors: false + checkExhaustiveness: false + # when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]' + excludes: '' processors: active: true exclude: + - 'DetektProgressListener' + # - 'KtFileCountProcessor' + # - 'PackageCountProcessor' + # - 'ClassCountProcessor' # - 'FunctionCountProcessor' # - 'PropertyCountProcessor' - # - 'ClassCountProcessor' - # - 'PackageCountProcessor' - # - 'KtFileCountProcessor' + # - 'ProjectComplexityProcessor' + # - 'ProjectCognitiveComplexityProcessor' + # - 'ProjectLLOCProcessor' + # - 'ProjectCLOCProcessor' + # - 'ProjectLOCProcessor' + # - 'ProjectSLOCProcessor' + # - 'LicenseHeaderLoaderExtension' console-reports: active: true exclude: - # - 'ProjectStatisticsReport' - # - 'ComplexityReport' - # - 'NotificationReport' - # - 'FindingsReport' - # - 'BuildFailureReport' + - 'ProjectStatisticsReport' + - 'ComplexityReport' + - 'NotificationReport' + - 'FindingsReport' + - 'FileBasedFindingsReport' + # - 'LiteFindingsReport' + +output-reports: + active: true + exclude: + # - 'TxtOutputReport' + # - 'XmlOutputReport' + # - 'HtmlOutputReport' + # - 'MdOutputReport' + # - 'SarifOutputReport' comments: active: true - excludes: - - "**/test/**" - - "**/androidTest/**" - - "**/*.Test.kt" - - "**/*.Spec.kt" - - "**/*.Spek.kt" + AbsentOrWrongFileLicense: + active: false + licenseTemplateFile: 'license.template' + licenseTemplateIsRegex: false CommentOverPrivateFunction: active: false CommentOverPrivateProperty: active: false + DeprecatedBlockTag: + active: false EndOfSentenceFormat: active: false - endOfSentenceFormat: ([.?!][ \t\n\r\f<])|([.?!:]$) + endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)' + KDocReferencesNonPublicProperty: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + OutdatedDocumentation: + active: false + matchTypeParameters: true + matchDeclarationsOrder: true + allowParamOnConstructorProperties: false UndocumentedPublicClass: active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] searchInNestedClass: true searchInInnerClass: true searchInInnerObject: true searchInInnerInterface: true + searchInProtectedClass: false UndocumentedPublicFunction: active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + searchProtectedFunction: false + UndocumentedPublicProperty: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + searchProtectedProperty: false complexity: active: true + CognitiveComplexMethod: + active: false + threshold: 15 ComplexCondition: active: true threshold: 4 @@ -57,14 +103,27 @@ complexity: active: false threshold: 10 includeStaticDeclarations: false + includePrivateDeclarations: false + ignoreOverloaded: false CyclomaticComplexMethod: active: true - threshold: 10 + threshold: 15 ignoreSingleWhenExpression: false ignoreSimpleWhenEntries: false + ignoreNestingFunctions: false + nestingFunctions: + - 'also' + - 'apply' + - 'forEach' + - 'isNotNull' + - 'ifNull' + - 'let' + - 'run' + - 'use' + - 'with' LabeledExpression: active: false - # ignoredLabels: "" + ignoredLabels: [] LargeClass: active: true threshold: 600 @@ -73,49 +132,77 @@ complexity: threshold: 60 LongParameterList: active: true - functionThreshold: 7 + functionThreshold: 6 constructorThreshold: 7 ignoreDefaultParameters: false + ignoreDataClasses: true + ignoreAnnotatedParameter: [] MethodOverloading: active: false threshold: 6 + NamedArguments: + active: false + threshold: 3 + ignoreArgumentsMatchingNames: false NestedBlockDepth: active: true threshold: 4 + NestedScopeFunctions: + active: false + threshold: 1 + functions: + - 'kotlin.apply' + - 'kotlin.run' + - 'kotlin.with' + - 'kotlin.let' + - 'kotlin.also' + ReplaceSafeCallChainWithRun: + active: false StringLiteralDuplication: active: false - excludes: - - "**/test/**" - - "**/androidTest/**" - - "**/*.Test.kt" - - "**/*.Spec.kt" - - "**/*.Spek.kt" + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] threshold: 3 ignoreAnnotation: true excludeStringsWithLessThan5Characters: true ignoreStringsRegex: '$^' TooManyFunctions: active: true - excludes: - - "**/test/**" - - "**/androidTest/**" - - "**/*.Test.kt" - - "**/*.Spec.kt" - - "**/*.Spek.kt" + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] thresholdInFiles: 11 thresholdInClasses: 11 - thresholdInInterfaces: 15 + thresholdInInterfaces: 11 thresholdInObjects: 11 thresholdInEnums: 11 ignoreDeprecated: false ignorePrivate: false ignoreOverridden: false +coroutines: + active: true + GlobalCoroutineUsage: + active: false + InjectDispatcher: + active: true + dispatcherNames: + - 'IO' + - 'Default' + - 'Unconfined' + RedundantSuspendModifier: + active: true + SleepInsteadOfDelay: + active: true + SuspendFunSwallowedCancellation: + active: false + SuspendFunWithCoroutineScopeReceiver: + active: false + SuspendFunWithFlowReturnType: + active: true + empty-blocks: active: true EmptyCatchBlock: active: true - allowedExceptionNameRegex: "^(_|(ignore|expected).*)" + allowedExceptionNameRegex: '_|(ignore|expected).*' EmptyClassBlock: active: true EmptyDefaultConstructor: @@ -139,6 +226,8 @@ empty-blocks: active: true EmptySecondaryConstructor: active: true + EmptyTryBlock: + active: true EmptyWhenBlock: active: true EmptyWhileBlock: @@ -147,216 +236,146 @@ empty-blocks: exceptions: active: true ExceptionRaisedInUnexpectedLocation: - active: false + active: true methodNames: - - toString - - hashCode - - equals - - finalize + - 'equals' + - 'finalize' + - 'hashCode' + - 'toString' InstanceOfCheckForException: - active: false - excludes: - - "**/test/**" - - "**/androidTest/**" - - "**/*.Test.kt" - - "**/*.Spec.kt" - - "**/*.Spek.kt" + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] NotImplementedDeclaration: active: false - PrintStackTrace: + ObjectExtendsThrowable: active: false + PrintStackTrace: + active: true RethrowCaughtException: - active: false + active: true ReturnFromFinally: - active: false + active: true + ignoreLabeled: false SwallowedException: - active: false + active: true ignoredExceptionTypes: - - InterruptedException - - NumberFormatException - - ParseException - - MalformedURLException + - 'InterruptedException' + - 'MalformedURLException' + - 'NumberFormatException' + - 'ParseException' + allowedExceptionNameRegex: '_|(ignore|expected).*' ThrowingExceptionFromFinally: - active: false + active: true ThrowingExceptionInMain: active: false ThrowingExceptionsWithoutMessageOrCause: - active: false + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] exceptions: - - IllegalArgumentException - - IllegalStateException - - IOException + - 'ArrayIndexOutOfBoundsException' + - 'Exception' + - 'IllegalArgumentException' + - 'IllegalMonitorStateException' + - 'IllegalStateException' + - 'IndexOutOfBoundsException' + - 'NullPointerException' + - 'RuntimeException' + - 'Throwable' ThrowingNewInstanceOfSameException: - active: false + active: true TooGenericExceptionCaught: active: true - excludes: - - "**/test/**" - - "**/androidTest/**" - - "**/*.Test.kt" - - "**/*.Spec.kt" - - "**/*.Spek.kt" + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] exceptionNames: - - ArrayIndexOutOfBoundsException - - Error - - Exception - - IllegalMonitorStateException - - NullPointerException - - IndexOutOfBoundsException - - RuntimeException - - Throwable - allowedExceptionNameRegex: "^(_|(ignore|expected).*)" + - 'ArrayIndexOutOfBoundsException' + - 'Error' + - 'Exception' + - 'IllegalMonitorStateException' + - 'IndexOutOfBoundsException' + - 'NullPointerException' + - 'RuntimeException' + - 'Throwable' + allowedExceptionNameRegex: '_|(ignore|expected).*' TooGenericExceptionThrown: active: true exceptionNames: - - Error - - Exception - - Throwable - - RuntimeException + - 'Error' + - 'Exception' + - 'RuntimeException' + - 'Throwable' naming: active: true + BooleanPropertyNaming: + active: false + allowedPattern: '^(is|has|are)' ClassNaming: active: true - excludes: - - "**/test/**" - - "**/androidTest/**" - - "**/*.Test.kt" - - "**/*.Spec.kt" - - "**/*.Spek.kt" - classPattern: '[A-Z$][a-zA-Z0-9$]*' + classPattern: '[A-Z][a-zA-Z0-9]*' ConstructorParameterNaming: active: true - excludes: - - "**/test/**" - - "**/androidTest/**" - - "**/*.Test.kt" - - "**/*.Spec.kt" - - "**/*.Spek.kt" parameterPattern: '[a-z][A-Za-z0-9]*' privateParameterPattern: '[a-z][A-Za-z0-9]*' excludeClassPattern: '$^' EnumNaming: active: true - excludes: - - "**/test/**" - - "**/androidTest/**" - - "**/*.Test.kt" - - "**/*.Spec.kt" - - "**/*.Spek.kt" - enumEntryPattern: '^[A-Z][_a-zA-Z0-9]*' + enumEntryPattern: '[A-Z][_a-zA-Z0-9]*' ForbiddenClassName: active: false - excludes: - - "**/test/**" - - "**/androidTest/**" - - "**/*.Test.kt" - - "**/*.Spec.kt" - - "**/*.Spek.kt" - # forbiddenName: '' + forbiddenName: [] FunctionMaxLength: active: false - excludes: - - "**/test/**" - - "**/androidTest/**" - - "**/*.Test.kt" - - "**/*.Spec.kt" - - "**/*.Spek.kt" maximumFunctionNameLength: 30 FunctionMinLength: active: false - excludes: - - "**/test/**" - - "**/androidTest/**" - - "**/*.Test.kt" - - "**/*.Spec.kt" - - "**/*.Spek.kt" minimumFunctionNameLength: 3 FunctionNaming: active: true - excludes: - - "**/test/**" - - "**/androidTest/**" - - "**/*.Test.kt" - - "**/*.Spec.kt" - - "**/*.Spek.kt" - functionPattern: '^([a-z$][a-zA-Z$0-9]*)|(`.*`)$' + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + functionPattern: '[a-z][a-zA-Z0-9]*' excludeClassPattern: '$^' FunctionParameterNaming: active: true - excludes: - - "**/test/**" - - "**/androidTest/**" - - "**/*.Test.kt" - - "**/*.Spec.kt" - - "**/*.Spek.kt" parameterPattern: '[a-z][A-Za-z0-9]*' excludeClassPattern: '$^' InvalidPackageDeclaration: - active: false + active: true rootPackage: '' + requireRootInDeclaration: false + LambdaParameterNaming: + active: false + parameterPattern: '[a-z][A-Za-z0-9]*|_' MatchingDeclarationName: active: true + mustBeFirst: true MemberNameEqualsClassName: - active: false + active: true ignoreOverridden: true + NoNameShadowing: + active: true + NonBooleanPropertyPrefixedWithIs: + active: false ObjectPropertyNaming: active: true - excludes: - - "**/test/**" - - "**/androidTest/**" - - "**/*.Test.kt" - - "**/*.Spec.kt" - - "**/*.Spek.kt" constantPattern: '[A-Za-z][_A-Za-z0-9]*' propertyPattern: '[A-Za-z][_A-Za-z0-9]*' privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*' PackageNaming: active: true - excludes: - - "**/test/**" - - "**/androidTest/**" - - "**/*.Test.kt" - - "**/*.Spec.kt" - - "**/*.Spek.kt" - packagePattern: '^[a-z]+(\.[a-z][A-Za-z0-9]*)*$' + packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*' TopLevelPropertyNaming: active: true - excludes: - - "**/test/**" - - "**/androidTest/**" - - "**/*.Test.kt" - - "**/*.Spec.kt" - - "**/*.Spek.kt" constantPattern: '[A-Z][_A-Z0-9]*' propertyPattern: '[A-Za-z][_A-Za-z0-9]*' privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*' VariableMaxLength: active: false - excludes: - - "**/test/**" - - "**/androidTest/**" - - "**/*.Test.kt" - - "**/*.Spec.kt" - - "**/*.Spek.kt" maximumVariableNameLength: 64 VariableMinLength: active: false - excludes: - - "**/test/**" - - "**/androidTest/**" - - "**/*.Test.kt" - - "**/*.Spec.kt" - - "**/*.Spek.kt" minimumVariableNameLength: 1 VariableNaming: active: true - excludes: - - "**/test/**" - - "**/androidTest/**" - - "**/*.Test.kt" - - "**/*.Spec.kt" - - "**/*.Spek.kt" variablePattern: '[a-z][A-Za-z0-9]*' privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' excludeClassPattern: '$^' @@ -364,213 +383,672 @@ naming: performance: active: true ArrayPrimitive: + active: true + CouldBeSequence: active: false + threshold: 3 ForEachOnRange: active: true - excludes: - - "**/test/**" - - "**/androidTest/**" - - "**/*.Test.kt" - - "**/*.Spec.kt" - - "**/*.Spek.kt" + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] SpreadOperator: active: true - excludes: - - "**/test/**" - - "**/androidTest/**" - - "**/*.Test.kt" - - "**/*.Spec.kt" - - "**/*.Spek.kt" + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + UnnecessaryPartOfBinaryExpression: + active: false UnnecessaryTemporaryInstantiation: active: true potential-bugs: active: true - EqualsAlwaysReturnsTrueOrFalse: + AvoidReferentialEquality: + active: true + forbiddenTypePatterns: + - 'kotlin.String' + CastNullableToNonNullableType: + active: false + CastToNullableType: + active: false + Deprecation: + active: false + DontDowncastCollectionTypes: active: false + DoubleMutabilityForCollection: + active: true + mutableTypes: + - 'kotlin.collections.MutableList' + - 'kotlin.collections.MutableMap' + - 'kotlin.collections.MutableSet' + - 'java.util.ArrayList' + - 'java.util.LinkedHashSet' + - 'java.util.HashSet' + - 'java.util.LinkedHashMap' + - 'java.util.HashMap' + ElseCaseInsteadOfExhaustiveWhen: + active: false + ignoredSubjectTypes: [] + EqualsAlwaysReturnsTrueOrFalse: + active: true EqualsWithHashCodeExist: active: true + ExitOutsideMain: + active: false ExplicitGarbageCollectionCall: active: true - InvalidRange: + HasPlatformType: + active: true + IgnoredReturnValue: + active: true + restrictToConfig: true + returnValueAnnotations: + - 'CheckResult' + - '*.CheckResult' + - 'CheckReturnValue' + - '*.CheckReturnValue' + ignoreReturnValueAnnotations: + - 'CanIgnoreReturnValue' + - '*.CanIgnoreReturnValue' + returnValueTypes: + - 'kotlin.sequences.Sequence' + - 'kotlinx.coroutines.flow.*Flow' + - 'java.util.stream.*Stream' + ignoreFunctionCall: [] + ImplicitDefaultLocale: + active: true + ImplicitUnitReturnType: active: false + allowExplicitReturnType: true + InvalidRange: + active: true IteratorHasNextCallsNextMethod: - active: false + active: true IteratorNotThrowingNoSuchElementException: - active: false + active: true LateinitUsage: active: false - excludes: - - "**/test/**" - - "**/androidTest/**" - - "**/*.Test.kt" - - "**/*.Spec.kt" - - "**/*.Spek.kt" - ignoreAnnotated: - ignoreOnClassesPattern: "" + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + ignoreOnClassesPattern: '' + MapGetWithNotNullAssertionOperator: + active: true + MissingPackageDeclaration: + active: false + excludes: ['**/*.kts'] + NullCheckOnMutableProperty: + active: false + NullableToStringCall: + active: false + PropertyUsedBeforeDeclaration: + active: false UnconditionalJumpStatementInLoop: active: false + UnnecessaryNotNullCheck: + active: false + UnnecessaryNotNullOperator: + active: true + UnnecessarySafeCall: + active: true + UnreachableCatchBlock: + active: true UnreachableCode: active: true UnsafeCallOnNullableType: - active: false + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] UnsafeCast: - active: false + active: true + UnusedUnaryOperator: + active: true UselessPostfixExpression: - active: false + active: true WrongEqualsTypeParameter: - active: false + active: true style: active: true + AlsoCouldBeApply: + active: false + BracesOnIfStatements: + active: false + singleLine: 'never' + multiLine: 'always' + BracesOnWhenStatements: + active: false + singleLine: 'necessary' + multiLine: 'consistent' + CanBeNonNullable: + active: false + CascadingCallWrapping: + active: false + includeElvis: true + ClassOrdering: + active: false CollapsibleIfStatements: active: false DataClassContainsFunctions: active: false - conversionFunctionPrefix: [ 'to' ] + conversionFunctionPrefix: + - 'to' + allowOperators: false DataClassShouldBeImmutable: active: false + DestructuringDeclarationWithTooManyEntries: + active: true + maxDestructuringEntries: 3 + DoubleNegativeLambda: + active: false + negativeFunctions: + - reason: 'Use `takeIf` instead.' + value: 'takeUnless' + - reason: 'Use `all` instead.' + value: 'none' + negativeFunctionNameParts: + - 'not' + - 'non' EqualsNullCall: - active: false + active: true EqualsOnSignatureLine: active: false - ExplicitItLambdaParameter: + ExplicitCollectionElementAccessMethod: active: false + ExplicitItLambdaParameter: + active: true ExpressionBodySyntax: active: false includeLineWrapping: false + ForbiddenAnnotation: + active: false + annotations: + - reason: 'it is a java annotation. Use `Suppress` instead.' + value: 'java.lang.SuppressWarnings' + - reason: 'it is a java annotation. Use `kotlin.Deprecated` instead.' + value: 'java.lang.Deprecated' + - reason: 'it is a java annotation. Use `kotlin.annotation.MustBeDocumented` instead.' + value: 'java.lang.annotation.Documented' + - reason: 'it is a java annotation. Use `kotlin.annotation.Target` instead.' + value: 'java.lang.annotation.Target' + - reason: 'it is a java annotation. Use `kotlin.annotation.Retention` instead.' + value: 'java.lang.annotation.Retention' + - reason: 'it is a java annotation. Use `kotlin.annotation.Repeatable` instead.' + value: 'java.lang.annotation.Repeatable' + - reason: 'Kotlin does not support @Inherited annotation, see https://youtrack.jetbrains.com/issue/KT-22265' + value: 'java.lang.annotation.Inherited' ForbiddenComment: - active: false - comments : - - TODO - - FIXME - - STOPSHIP + active: true + comments: + - reason: 'Forbidden FIXME todo marker in comment, please fix the problem.' + value: 'FIXME:' + - reason: 'Forbidden STOPSHIP todo marker in comment, please address the problem before shipping the code.' + value: 'STOPSHIP:' + - reason: 'Forbidden TODO todo marker in comment, please do the changes.' + value: 'TODO:' + allowedPatterns: '' ForbiddenImport: active: false - imports: - ForbiddenVoid: + imports: [] + forbiddenPatterns: '' + ForbiddenMethodCall: + active: false + methods: + - reason: 'print does not allow you to configure the output stream. Use a logger instead.' + value: 'kotlin.io.print' + - reason: 'println does not allow you to configure the output stream. Use a logger instead.' + value: 'kotlin.io.println' + ForbiddenSuppress: active: false + rules: [] + ForbiddenVoid: + active: true ignoreOverridden: false + ignoreUsageInGenerics: false FunctionOnlyReturningConstant: - active: false + active: true ignoreOverridableFunction: true - excludedFunctions: ['describeContents' ] + ignoreActualFunction: true + excludedFunctions: [] LoopWithTooManyJumpStatements: - active: false + active: true maxJumpCount: 1 MagicNumber: active: true - excludes: - - "**/test/**" - - "**/androidTest/**" - - "**/*.Test.kt" - - "**/*.Spec.kt" - - "**/*.Spek.kt" + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/*.kts'] ignoreNumbers: - - "-1" - - "0" - - "1" - - "2" + - '-1' + - '0' + - '1' + - '2' ignoreHashCodeFunction: true ignorePropertyDeclaration: false + ignoreLocalVariableDeclaration: false ignoreConstantDeclaration: true ignoreCompanionObjectPropertyDeclaration: true ignoreAnnotation: false ignoreNamedArgument: true ignoreEnums: false ignoreRanges: false + ignoreExtensionFunctions: true + MandatoryBracesLoops: + active: false + MaxChainedCallsOnSameLine: + active: false + maxChainedCalls: 5 MaxLineLength: active: true maxLineLength: 120 excludePackageStatements: true excludeImportStatements: true excludeCommentStatements: false + excludeRawStrings: true MayBeConst: - active: false + active: true ModifierOrder: active: true - NestedClassesVisibility: + MultilineLambdaItParameter: active: false - NewLineAtEndOfFile: + MultilineRawStringIndentation: active: false + indentSize: 4 + trimmingMethods: + - 'trimIndent' + - 'trimMargin' + NestedClassesVisibility: + active: true + NewLineAtEndOfFile: + active: true NoTabs: active: false + NullableBooleanCheck: + active: false + ObjectLiteralToLambda: + active: true OptionalAbstractKeyword: active: true OptionalUnit: active: false - BracesOnWhenStatements: - active: false PreferToOverPairSyntax: active: false ProtectedMemberInFinalClass: + active: true + RedundantExplicitType: active: false + RedundantHigherOrderMapUsage: + active: true RedundantVisibilityModifierRule: active: false ReturnCount: active: true - max: 3 - excludedFunctions: [ "equals" ] + max: 2 + excludedFunctions: + - 'equals' excludeLabeled: false excludeReturnFromLambda: true + excludeGuardClauses: false SafeCast: active: true SerialVersionUIDInSerializableClass: - active: false + active: true SpacingBetweenPackageAndImports: active: false + StringShouldBeRawString: + active: false + maxEscapedCharacterCount: 2 + ignoredCharacters: [] ThrowsCount: active: true max: 2 + excludeGuardClauses: false TrailingWhitespace: active: false + TrimMultilineRawString: + active: false + trimmingMethods: + - 'trimIndent' + - 'trimMargin' UnderscoresInNumericLiterals: active: false - acceptableLength: 5 + acceptableLength: 4 + allowNonStandardGrouping: false UnnecessaryAbstractClass: + active: true + UnnecessaryAnnotationUseSiteTarget: active: false - ignoreAnnotated: - - dagger.Module UnnecessaryApply: + active: true + UnnecessaryBackticks: active: false + UnnecessaryBracesAroundTrailingLambda: + active: false + UnnecessaryFilter: + active: true UnnecessaryInheritance: + active: true + UnnecessaryInnerClass: active: false UnnecessaryLet: active: false UnnecessaryParentheses: active: false + allowForUnclearPrecedence: false UntilInsteadOfRangeTo: active: false UnusedImports: active: false UnusedParameter: - active: false + active: true + allowedNames: 'ignored|expected' UnusedPrivateClass: - active: false + active: true UnusedPrivateMember: - active: false - allowedNames: "(_|ignored|expected|serialVersionUID)" + active: true + allowedNames: '' + UnusedPrivateProperty: + active: true + allowedNames: '_|ignored|expected|serialVersionUID' + UseAnyOrNoneInsteadOfFind: + active: true + UseArrayLiteralsInAnnotations: + active: true + UseCheckNotNull: + active: true UseCheckOrError: - active: false + active: true UseDataClass: active: false - ignoreAnnotated: + allowVars: false + UseEmptyCounterpart: + active: false + UseIfEmptyOrIfBlank: + active: false + UseIfInsteadOfWhen: + active: false + ignoreWhenContainingVariableDeclaration: false + UseIsNullOrEmpty: + active: true + UseLet: + active: false + UseOrEmpty: + active: true UseRequire: + active: true + UseRequireNotNull: + active: true + UseSumOfInsteadOfFlatMapSize: active: false UselessCallOnNotNull: - active: false + active: true UtilityClassWithPublicConstructor: - active: false + active: true VarCouldBeVal: - active: false + active: true + ignoreLateinitVar: false WildcardImport: active: true - excludes: - - "**/test/**" - - "**/androidTest/**" - - "**/*.Test.kt" - - "**/*.Spec.kt" - - "**/*.Spek.kt" excludeImports: - - java.util.* - - kotlinx.android.synthetic.* + - 'java.util.*' + +formatting: + active: true + android: false + autoCorrect: true + AnnotationOnSeparateLine: + active: true + autoCorrect: true + indentSize: 4 + AnnotationSpacing: + active: true + autoCorrect: true + ArgumentListWrapping: + active: true + autoCorrect: true + indentSize: 4 + maxLineLength: 120 + BlockCommentInitialStarAlignment: + active: true + autoCorrect: true + ChainWrapping: + active: true + autoCorrect: true + indentSize: 4 + ClassName: + active: false + CommentSpacing: + active: true + autoCorrect: true + CommentWrapping: + active: true + autoCorrect: true + indentSize: 4 + ContextReceiverMapping: + active: false + autoCorrect: true + maxLineLength: 120 + indentSize: 4 + DiscouragedCommentLocation: + active: false + autoCorrect: true + EnumEntryNameCase: + active: true + autoCorrect: true + EnumWrapping: + active: false + autoCorrect: true + indentSize: 4 + Filename: + active: true + FinalNewline: + active: true + autoCorrect: true + insertFinalNewLine: true + FunKeywordSpacing: + active: true + autoCorrect: true + FunctionName: + active: false + FunctionReturnTypeSpacing: + active: true + autoCorrect: true + maxLineLength: 120 + FunctionSignature: + active: false + autoCorrect: true + forceMultilineWhenParameterCountGreaterOrEqualThan: 2147483647 + functionBodyExpressionWrapping: 'default' + maxLineLength: 120 + indentSize: 4 + FunctionStartOfBodySpacing: + active: true + autoCorrect: true + FunctionTypeReferenceSpacing: + active: true + autoCorrect: true + IfElseBracing: + active: false + autoCorrect: true + indentSize: 4 + IfElseWrapping: + active: false + autoCorrect: true + indentSize: 4 + ImportOrdering: + active: true + autoCorrect: true + layout: '*,java.**,javax.**,kotlin.**,^' + Indentation: + active: true + autoCorrect: true + indentSize: 4 + KdocWrapping: + active: true + autoCorrect: true + indentSize: 4 + MaximumLineLength: + active: true + maxLineLength: 120 + ignoreBackTickedIdentifier: false + ModifierListSpacing: + active: true + autoCorrect: true + ModifierOrdering: + active: true + autoCorrect: true + MultiLineIfElse: + active: true + autoCorrect: true + indentSize: 4 + MultilineExpressionWrapping: + active: false + autoCorrect: true + indentSize: 4 + NoBlankLineBeforeRbrace: + active: true + autoCorrect: true + NoBlankLineInList: + active: false + autoCorrect: true + NoBlankLinesInChainedMethodCalls: + active: true + autoCorrect: true + NoConsecutiveBlankLines: + active: true + autoCorrect: true + NoConsecutiveComments: + active: false + NoEmptyClassBody: + active: true + autoCorrect: true + NoEmptyFirstLineInClassBody: + active: false + autoCorrect: true + indentSize: 4 + NoEmptyFirstLineInMethodBlock: + active: true + autoCorrect: true + NoLineBreakAfterElse: + active: true + autoCorrect: true + NoLineBreakBeforeAssignment: + active: true + autoCorrect: true + NoMultipleSpaces: + active: true + autoCorrect: true + NoSemicolons: + active: true + autoCorrect: true + NoSingleLineBlockComment: + active: false + autoCorrect: true + indentSize: 4 + NoTrailingSpaces: + active: true + autoCorrect: true + NoUnitReturn: + active: true + autoCorrect: true + NoUnusedImports: + active: true + autoCorrect: true + NoWildcardImports: + active: true + packagesToUseImportOnDemandProperty: 'java.util.*,kotlinx.android.synthetic.**' + NullableTypeSpacing: + active: true + autoCorrect: true + PackageName: + active: true + autoCorrect: true + ParameterListSpacing: + active: false + autoCorrect: true + ParameterListWrapping: + active: true + autoCorrect: true + maxLineLength: 120 + indentSize: 4 + ParameterWrapping: + active: true + autoCorrect: true + indentSize: 4 + maxLineLength: 120 + PropertyName: + active: false + PropertyWrapping: + active: true + autoCorrect: true + indentSize: 4 + maxLineLength: 120 + SpacingAroundAngleBrackets: + active: true + autoCorrect: true + SpacingAroundColon: + active: true + autoCorrect: true + SpacingAroundComma: + active: true + autoCorrect: true + SpacingAroundCurly: + active: true + autoCorrect: true + SpacingAroundDot: + active: true + autoCorrect: true + SpacingAroundDoubleColon: + active: true + autoCorrect: true + SpacingAroundKeyword: + active: true + autoCorrect: true + SpacingAroundOperators: + active: true + autoCorrect: true + SpacingAroundParens: + active: true + autoCorrect: true + SpacingAroundRangeOperator: + active: true + autoCorrect: true + SpacingAroundUnaryOperator: + active: true + autoCorrect: true + SpacingBetweenDeclarationsWithAnnotations: + active: true + autoCorrect: true + SpacingBetweenDeclarationsWithComments: + active: true + autoCorrect: true + SpacingBetweenFunctionNameAndOpeningParenthesis: + active: true + autoCorrect: true + StringTemplate: + active: true + autoCorrect: true + StringTemplateIndent: + active: false + autoCorrect: true + indentSize: 4 + TrailingCommaOnCallSite: + active: true + autoCorrect: true + useTrailingCommaOnCallSite: true + TrailingCommaOnDeclarationSite: + active: true + autoCorrect: true + useTrailingCommaOnDeclarationSite: true + TryCatchFinallySpacing: + active: false + autoCorrect: true + indentSize: 4 + TypeArgumentListSpacing: + active: false + autoCorrect: true + indentSize: 4 + TypeParameterListSpacing: + active: false + autoCorrect: true + indentSize: 4 + UnnecessaryParenthesesBeforeTrailingLambda: + active: true + autoCorrect: true + Wrapping: + active: true + autoCorrect: true + indentSize: 4 + maxLineLength: 120 diff --git a/settings.gradle.kts b/settings.gradle.kts index 5bc2375..709d3ee 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,7 +2,6 @@ pluginManagement { val kotlinVersion: String by settings val gradleDependencyManagementVersion: String by settings val detektVersion: String by settings - val ktlintVersion: String by settings val gitPropertiesVersion: String by settings val palantirGitVersion: String by settings val jGitVerVersion: String by settings @@ -15,7 +14,6 @@ pluginManagement { id("io.codearte.nexus-staging") version nexusStagingVersion apply false id("io.spring.dependency-management") version gradleDependencyManagementVersion apply false id("io.gitlab.arturbosch.detekt") version detektVersion apply false - id("org.jlleitschuh.gradle.ktlint") version ktlintVersion apply false id("com.palantir.git-version") version palantirGitVersion apply false id("com.gorylenko.gradle-git-properties") version gitPropertiesVersion apply false id("jacoco") diff --git a/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/annotation/ContractAction.kt b/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/annotation/ContractAction.kt index 191d293..1c20caa 100644 --- a/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/annotation/ContractAction.kt +++ b/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/annotation/ContractAction.kt @@ -9,5 +9,5 @@ import java.lang.annotation.Inherited @Target(AnnotationTarget.FUNCTION) @Inherited annotation class ContractAction( - val name: String = "" + val name: String = "", ) diff --git a/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/annotation/ContractInit.kt b/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/annotation/ContractInit.kt index ad8a692..0ec01bf 100644 --- a/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/annotation/ContractInit.kt +++ b/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/annotation/ContractInit.kt @@ -9,5 +9,5 @@ import java.lang.annotation.Inherited @Target(AnnotationTarget.FUNCTION) @Inherited annotation class ContractInit( - val name: String = "" + val name: String = "", ) diff --git a/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/annotation/InvokeParam.kt b/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/annotation/InvokeParam.kt index 0934a9b..92e4b66 100644 --- a/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/annotation/InvokeParam.kt +++ b/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/annotation/InvokeParam.kt @@ -8,5 +8,5 @@ package com.wavesenterprise.sdk.contract.api.annotation @Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.VALUE_PARAMETER) annotation class InvokeParam( - val name: String + val name: String, ) diff --git a/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/domain/ContractConcurrency.kt b/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/domain/ContractConcurrency.kt index d8cb1a6..25246b1 100644 --- a/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/domain/ContractConcurrency.kt +++ b/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/domain/ContractConcurrency.kt @@ -17,7 +17,7 @@ enum class ContractConcurrency { /** * Default value for single-threaded contracts */ - OFF + OFF, } private const val IO_ASYNC_MULTIPLIER = 4 diff --git a/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/domain/DefaultContractCall.kt b/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/domain/DefaultContractCall.kt index a2b5f54..715c2d0 100644 --- a/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/domain/DefaultContractCall.kt +++ b/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/domain/DefaultContractCall.kt @@ -11,13 +11,13 @@ import com.wavesenterprise.sdk.node.domain.contract.ContractTransaction data class DefaultContractCall( override val id: TxId, override val sender: Address, - override val timestamp: Timestamp + override val timestamp: Timestamp, ) : ContractCall { constructor(tx: ContractTransaction) : this( id = tx.id, sender = tx.sender, - tx.timestamp + tx.timestamp, ) override val caller: String diff --git a/we-contract-sdk-client/we-contract-sdk-blocking-client/src/main/kotlin/com/wavesenterprise/sdk/contract/client/invocation/factory/ContractBlockingClientFactory.kt b/we-contract-sdk-client/we-contract-sdk-blocking-client/src/main/kotlin/com/wavesenterprise/sdk/contract/client/invocation/factory/ContractBlockingClientFactory.kt index 874732b..72b9679 100644 --- a/we-contract-sdk-client/we-contract-sdk-blocking-client/src/main/kotlin/com/wavesenterprise/sdk/contract/client/invocation/factory/ContractBlockingClientFactory.kt +++ b/we-contract-sdk-client/we-contract-sdk-blocking-client/src/main/kotlin/com/wavesenterprise/sdk/contract/client/invocation/factory/ContractBlockingClientFactory.kt @@ -41,7 +41,7 @@ open class ContractBlockingClientFactory( private val toDataValueConverter = converterFactory.toDataValueConverter() private val fromDataEntryConverter = converterFactory.fromDataEntryConverter() private val nodeContractStateProvider = BlockingClientNodeContractStateValuesProvider( - contractService = contractService + contractService = contractService, ) private val contractStateFactory = DefaultBackingMapContractStateFactory( nodeContractStateValuesProvider = nodeContractStateProvider, @@ -95,19 +95,19 @@ open class ContractBlockingClientFactory( contractInterface.classLoader, arrayOf(contractInterface), contractHandlerInvocationHandler, - ) as S + ) as S, ) return ExecutionContext( tx = requireNotNull(resultTx) { "Result tx is null. Probably you have passed an empty receiver." - } + }, ) } private fun handleLocalContractValidation( contractStateFactory: DefaultBackingMapContractStateFactory, fromDataEntryConverter: ContractFromDataEntryConverter, - tx: ContractTx + tx: ContractTx, ) { if (!localContractValidation.isEnabled()) { return diff --git a/we-contract-sdk-client/we-contract-sdk-blocking-client/src/main/kotlin/com/wavesenterprise/sdk/contract/client/invocation/impl/ParamsBuilderImpl.kt b/we-contract-sdk-client/we-contract-sdk-blocking-client/src/main/kotlin/com/wavesenterprise/sdk/contract/client/invocation/impl/ParamsBuilderImpl.kt index fcb5389..b450048 100644 --- a/we-contract-sdk-client/we-contract-sdk-blocking-client/src/main/kotlin/com/wavesenterprise/sdk/contract/client/invocation/impl/ParamsBuilderImpl.kt +++ b/we-contract-sdk-client/we-contract-sdk-blocking-client/src/main/kotlin/com/wavesenterprise/sdk/contract/client/invocation/impl/ParamsBuilderImpl.kt @@ -20,7 +20,7 @@ class ParamsBuilderImpl( element = DataEntry( key = DataKey(ACTION_PARAM_KEY), value = converter.convert(actionName), - ) + ), ) args?.also { addAll(getInvokeParams(method.parameters, args)) } } @@ -32,7 +32,7 @@ class ParamsBuilderImpl( requireNotNull(args) DataEntry( key = DataKey(parameters[i].takeName()), - value = converter.convert(args[i]) + value = converter.convert(args[i]), ) } diff --git a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/dispatch/ApplierContractTxToState.kt b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/dispatch/ApplierContractTxToState.kt index e703624..09579fa 100644 --- a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/dispatch/ApplierContractTxToState.kt +++ b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/dispatch/ApplierContractTxToState.kt @@ -24,7 +24,7 @@ class ApplierContractTxToState( fun contractTxToState(contractTransaction: ContractTransaction): ContractState { logger.debug("Applying transaction with ID = ${contractTransaction.id.asBase58String()}") val contractState = contractStateFactory.buildContractState( - contractTransaction.contractId + contractTransaction.contractId, ) val contractHandlerFactory = ContractHandlerFactoryImpl( contractState = contractState, @@ -37,7 +37,7 @@ class ApplierContractTxToState( ).process(contractTransaction) logger.debug( "Successfully applied transaction with ID = ${contractTransaction.id.asBase58String()} " + - "to virtual state" + "to virtual state", ) return contractState } diff --git a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/dispatch/ContractDispatcher.kt b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/dispatch/ContractDispatcher.kt index c356bfa..c00c9ff 100644 --- a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/dispatch/ContractDispatcher.kt +++ b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/dispatch/ContractDispatcher.kt @@ -48,7 +48,7 @@ class ContractDispatcher( fun dispatch() { logger.info("Dispatching contract for handler class ${contractHandlerType.canonicalName}") val contractTransactionIterator = connectContractService.connect( - ConnectionRequest(connectionId) + ConnectionRequest(connectionId), ).iterator() contractTransactionIterator.forEachRemaining { contractTransactionResponse -> @@ -63,12 +63,12 @@ class ContractDispatcher( txContractService.commitExecutionSuccess( ExecutionSuccessRequest( txId = contractTransactionResponse.txId(), - results = contractState.results() - ) + results = contractState.results(), + ), ) logger.info { "Successfully applied transaction with ID = ${ - contractTransactionResponse.txId().asBase58String() + contractTransactionResponse.txId().asBase58String() } to state" } } catch (invocationTargetException: InvocationTargetException) { @@ -78,7 +78,7 @@ class ContractDispatcher( } tryCommitError( contractTransactionResponse = contractTransactionResponse, - cause = invocationTargetException.cause ?: invocationTargetException + cause = invocationTargetException.cause ?: invocationTargetException, ) } catch (ex: Exception) { logger.error(ex) { @@ -112,12 +112,12 @@ class ContractDispatcher( is RecoverableException -> ExecutionErrorRequest.ErrorCode.RECOVERABLE_ERROR else -> ExecutionErrorRequest.ErrorCode.FATAL_ERROR }, - ) + ), ) } catch (commitException: Exception) { logger.error( "Error while committing execution error for exception ${cause.javaClass.canonicalName}", - commitException + commitException, ) throw commitException } diff --git a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/dispatch/ContractTxToStateApplier.kt b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/dispatch/ContractTxToStateApplier.kt index 9ad5b56..a20fe10 100644 --- a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/dispatch/ContractTxToStateApplier.kt +++ b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/dispatch/ContractTxToStateApplier.kt @@ -24,7 +24,7 @@ class ContractTxToStateApplier( fun contractTxToState(contractTransaction: ContractTransaction): ContractState { logger.debug("Applying transaction with ID = ${contractTransaction.id.asBase58String()}") val contractState = contractStateFactory.buildContractState( - contractTransaction.contractId + contractTransaction.contractId, ) val contractHandlerFactory = ContractHandlerFactoryImpl( contractState = contractState, @@ -37,7 +37,7 @@ class ContractTxToStateApplier( ).process(contractTransaction) logger.debug( "Successfully applied transaction with ID = ${contractTransaction.id.asBase58String()} " + - "to virtual state" + "to virtual state", ) return contractState } diff --git a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/node/BlockingClientNodeContractStateValuesProvider.kt b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/node/BlockingClientNodeContractStateValuesProvider.kt index 29573e6..f955d3b 100644 --- a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/node/BlockingClientNodeContractStateValuesProvider.kt +++ b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/node/BlockingClientNodeContractStateValuesProvider.kt @@ -23,6 +23,6 @@ class BlockingClientNodeContractStateValuesProvider( keysFilter = KeysFilter(keys.toList()), offset = null, limit = null, - ) + ), ) } diff --git a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/process/ContractHandlerFactoryImpl.kt b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/process/ContractHandlerFactoryImpl.kt index 69aec9d..e45acf0 100644 --- a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/process/ContractHandlerFactoryImpl.kt +++ b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/process/ContractHandlerFactoryImpl.kt @@ -12,7 +12,7 @@ import java.lang.reflect.Constructor class ContractHandlerFactoryImpl( private val contractState: ContractState, - private val contractHandlerType: Class + private val contractHandlerType: Class, ) : ContractHandlerFactory { override fun createHandler(tx: ContractTransaction): T { @@ -27,7 +27,7 @@ class ContractHandlerFactoryImpl( @Suppress("UNCHECKED_CAST", "IMPLICIT_CAST_TO_ANY") private fun Constructor<*>.newInstanceFor( tx: ContractTransaction, - contractState: ContractState + contractState: ContractState, ) = run { if (parameterCount == 0) { newInstance() as T @@ -40,7 +40,7 @@ class ContractHandlerFactoryImpl( ContractCall::class.java -> DefaultContractCall(tx) else -> throw IllegalArgumentException( "Contract handler doesn't support ${paramClass.canonicalName} in its constructor arguments. " + - "Contract handler class - ${contractHandlerType.canonicalName}" + "Contract handler class - ${contractHandlerType.canonicalName}", ) } putWrc12MetaIntoResult(contractState, contractHandlerType, tx) diff --git a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/process/ContractHandlerInvocationExtractor.kt b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/process/ContractHandlerInvocationExtractor.kt index 090dfcd..d04b2e1 100644 --- a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/process/ContractHandlerInvocationExtractor.kt +++ b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/process/ContractHandlerInvocationExtractor.kt @@ -29,7 +29,7 @@ class ContractHandlerInvocationExtractor( contractHandlerType, ContractInit::class.java, true, - false + false, ).forEach { method -> method.getDeclaredAnnotation(ContractInit::class.java)?.also { initMethods.add(it.name.ifBlank { method.name } to method) @@ -39,7 +39,7 @@ class ContractHandlerInvocationExtractor( contractHandlerType, ContractAction::class.java, true, - false + false, ).forEach { method -> method.getDeclaredAnnotation(ContractAction::class.java)?.also { actionMethods.add(it.name.ifBlank { method.name } to method) @@ -48,7 +48,7 @@ class ContractHandlerInvocationExtractor( if (initMethods.isEmpty() && actionMethods.isEmpty()) { throw IllegalArgumentException( "Class ${contractHandlerType.canonicalName} had neither @ContractInit" + - " nor @ContractAction annotated methods" + " nor @ContractAction annotated methods", ) } initMethodByNameMap = initMethods.toMap() @@ -62,10 +62,10 @@ class ContractHandlerInvocationExtractor( is DataValue.StringDataValue -> dataEntryValue.value else -> throw IllegalArgumentException( "Only string data values a supported for params with 'action' key. " + - "ContractHandler - ${contractHandlerType.canonicalName}. Key value $value" + "ContractHandler - ${contractHandlerType.canonicalName}. Key value $value", ) } - } + }, ) { "Key 'action' has not been found in params of contract tx with ID = ${contractTransaction.id}. " + "Contract ID = ${contractTransaction.contractId} " diff --git a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateEx.kt b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateEx.kt index f8e16ba..8af363e 100644 --- a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateEx.kt +++ b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateEx.kt @@ -25,7 +25,7 @@ inline operator fun Mapping.set(key: String, value: T) { */ inline operator fun ContractState.getValue( thisRef: Any?, - property: KProperty<*> + property: KProperty<*>, ): T { val type = object : TypeReference() {}.getType() val name = keyName(property.name) @@ -68,7 +68,7 @@ inline operator fun ContractState.getValue( inline operator fun ContractState.setValue( thisRef: Any?, property: KProperty<*>, - value: T + value: T, ) { put(keyName(property.name), value as Any) } @@ -90,7 +90,7 @@ fun Type.isSimpleType() = } class DynamicTypeReference( - private val type: Type + private val type: Type, ) : TypeReference() { override fun getType(): Type { return type diff --git a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateImpl.kt b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateImpl.kt index f75b0a7..e2c3ef6 100644 --- a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateImpl.kt +++ b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateImpl.kt @@ -34,7 +34,7 @@ class ContractStateImpl( } // todo maybe move to DataEntry if used only by contract DataEntry( key = DataKey(key), - value = contractToDataValueConverter.convert(value) + value = contractToDataValueConverter.convert(value), ).also { executionResultMap[key] = it backingMap[key] = it diff --git a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateReaderIml.kt b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateReaderIml.kt index 9cd026a..a924456 100644 --- a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateReaderIml.kt +++ b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateReaderIml.kt @@ -63,26 +63,26 @@ class ContractStateReaderIml( ReadMappingForType( contractStateReader = this, keyMapper = PrefixedKeyMapper(*prefix), - type = type + type = type, ) override fun getMapping(typeReference: TypeReference, vararg prefix: String): ReadMapping = ReadMappingForTypeReference( contractStateReader = this, keyMapper = PrefixedKeyMapper(*prefix), - typeReference = typeReference + typeReference = typeReference, ) override fun getContractMeta(): Optional = tryGet(CONTRACT_META_KEY, WRC12Meta::class.java) private fun getAllWithEntryMapping( keys: Set, - entryMappingFn: (DataEntry) -> T + entryMappingFn: (DataEntry) -> T, ): Map { val keysToBeRequested = keys.minus(backingMap.keys) if (keysToBeRequested.isNotEmpty()) { backingMap.putAll( - nodeContractStateValuesProvider.getForKeys(contractId, keysToBeRequested).map { it.key.value to it } + nodeContractStateValuesProvider.getForKeys(contractId, keysToBeRequested).map { it.key.value to it }, ) } return backingMap.entries diff --git a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/SpecifiedContractStateProvider.kt b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/SpecifiedContractStateProvider.kt index e004a98..9cbec5a 100644 --- a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/SpecifiedContractStateProvider.kt +++ b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/SpecifiedContractStateProvider.kt @@ -3,7 +3,7 @@ package com.wavesenterprise.sdk.contract.core.state import com.wavesenterprise.sdk.contract.api.state.ContractState class SpecifiedContractStateProvider( - private val contractState: ContractState + private val contractState: ContractState, ) : ContractStateProvider { override fun getContractState(): ContractState = contractState } diff --git a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/mapping/PrefixedKeyMapper.kt b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/mapping/PrefixedKeyMapper.kt index 9651af7..208f749 100644 --- a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/mapping/PrefixedKeyMapper.kt +++ b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/mapping/PrefixedKeyMapper.kt @@ -4,7 +4,7 @@ import com.wavesenterprise.sdk.contract.api.domain.ContractKeys.PREFIX_KEY_DELIM import com.wavesenterprise.sdk.contract.api.state.mapping.KeyMapper class PrefixedKeyMapper( - prefixes: Set + prefixes: Set, ) : KeyMapper { private val prefixForKey: String diff --git a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/mapping/WriteMappingImpl.kt b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/mapping/WriteMappingImpl.kt index 7d94bd0..3c8d41b 100644 --- a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/mapping/WriteMappingImpl.kt +++ b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/mapping/WriteMappingImpl.kt @@ -6,13 +6,13 @@ import com.wavesenterprise.sdk.contract.api.state.mapping.WriteMapping class WriteMappingImpl( private val contractStateWriter: ContractStateWriter, - private val keyMapper: KeyMapper + private val keyMapper: KeyMapper, ) : WriteMapping { override fun put(key: String, value: T): WriteMapping = this.also { contractStateWriter.put( keyMapper.doMapping(key), - requireNotNull(value) { "Couldn't put null on contract state for key = $key" } + requireNotNull(value) { "Couldn't put null on contract state for key = $key" }, ) } } diff --git a/we-contract-sdk-grpc/src/main/kotlin/com/wavesenterprise/sdk/contract/grpc/GrpcJacksonContractDispatcherBuilder.kt b/we-contract-sdk-grpc/src/main/kotlin/com/wavesenterprise/sdk/contract/grpc/GrpcJacksonContractDispatcherBuilder.kt index 8d03924..6855fc4 100644 --- a/we-contract-sdk-grpc/src/main/kotlin/com/wavesenterprise/sdk/contract/grpc/GrpcJacksonContractDispatcherBuilder.kt +++ b/we-contract-sdk-grpc/src/main/kotlin/com/wavesenterprise/sdk/contract/grpc/GrpcJacksonContractDispatcherBuilder.kt @@ -97,14 +97,14 @@ class GrpcJacksonContractDispatcherBuilder { val actualConnectionProperties = grpcConnectionProperties ?: EnvGrpcConnectionProperties() val actualChannelProvider = channelProvider ?: DefaultGrpcChannelProvider() val channel = actualChannelProvider.getChannel( - grpcConnectionProperties = actualConnectionProperties + grpcConnectionProperties = actualConnectionProperties, ) val defaultTxContractService = getTxContractService(channel) val defaultConnectContractService = getConnectContractService(channel, actualConnectionProperties) val nodeContractStateProvider: NodeContractStateValuesProvider = BlockingClientNodeContractStateValuesProvider( - contractService = txContractService ?: defaultTxContractService + contractService = txContractService ?: defaultTxContractService, ) val contractStateFactory: ContractStateFactory = DefaultBackingMapContractStateFactory( @@ -128,7 +128,7 @@ class GrpcJacksonContractDispatcherBuilder { private fun defaultExecutor(actualContractConcurrency: ContractConcurrency): Executor = Executors.newFixedThreadPool( - actualContractConcurrency.toThreadCount(Runtime.getRuntime().availableProcessors()) + actualContractConcurrency.toThreadCount(Runtime.getRuntime().availableProcessors()), ) private fun defaultObjectMapper(): ObjectMapper = ObjectMapper() @@ -139,17 +139,17 @@ class GrpcJacksonContractDispatcherBuilder { ): ContractService = ContractGrpcBlockingService( channel = channel, clientInterceptors = listOf( - HeaderClientInterceptor(connectionProperties.authTokenSupplier()) - ) + HeaderClientInterceptor(connectionProperties.authTokenSupplier()), + ), ) private fun getTxContractService(channel: Channel): ContractService = ContractGrpcBlockingService( channel = channel, clientInterceptors = listOf( HeaderClientInterceptor( - ThreadLocalAuthTokenSupplier() - ) - ) + ThreadLocalAuthTokenSupplier(), + ), + ), ) private fun GrpcConnectionProperties.authTokenSupplier() = object : AuthTokenSupplier { diff --git a/we-contract-sdk-grpc/src/main/kotlin/com/wavesenterprise/sdk/contract/grpc/connect/EnvGrpcConnectionProperties.kt b/we-contract-sdk-grpc/src/main/kotlin/com/wavesenterprise/sdk/contract/grpc/connect/EnvGrpcConnectionProperties.kt index 02660a5..950d01e 100644 --- a/we-contract-sdk-grpc/src/main/kotlin/com/wavesenterprise/sdk/contract/grpc/connect/EnvGrpcConnectionProperties.kt +++ b/we-contract-sdk-grpc/src/main/kotlin/com/wavesenterprise/sdk/contract/grpc/connect/EnvGrpcConnectionProperties.kt @@ -30,7 +30,7 @@ class EnvGrpcConnectionProperties : GrpcConnectionProperties { } logger.debug( "Environment grpc connection properties - " + - "$nodeHost:$nodePort; connectionId = $connectionId; authToken = $authToken" + "$nodeHost:$nodePort; connectionId = $connectionId; authToken = $authToken", ) } } diff --git a/we-contract-sdk-grpc/src/main/kotlin/com/wavesenterprise/sdk/contract/grpc/connect/HeaderClientInterceptor.kt b/we-contract-sdk-grpc/src/main/kotlin/com/wavesenterprise/sdk/contract/grpc/connect/HeaderClientInterceptor.kt index 90630be..74b29e2 100644 --- a/we-contract-sdk-grpc/src/main/kotlin/com/wavesenterprise/sdk/contract/grpc/connect/HeaderClientInterceptor.kt +++ b/we-contract-sdk-grpc/src/main/kotlin/com/wavesenterprise/sdk/contract/grpc/connect/HeaderClientInterceptor.kt @@ -30,7 +30,7 @@ class HeaderClientInterceptor( super.onHeaders(headers) } }, - headers + headers, ) } } diff --git a/we-contract-sdk-jackson/src/main/kotlin/com/wavesenterprise/sdk/contract/jackson/JacksonFromDataEntryConverter.kt b/we-contract-sdk-jackson/src/main/kotlin/com/wavesenterprise/sdk/contract/jackson/JacksonFromDataEntryConverter.kt index 163b4a8..c1b583b 100644 --- a/we-contract-sdk-jackson/src/main/kotlin/com/wavesenterprise/sdk/contract/jackson/JacksonFromDataEntryConverter.kt +++ b/we-contract-sdk-jackson/src/main/kotlin/com/wavesenterprise/sdk/contract/jackson/JacksonFromDataEntryConverter.kt @@ -22,7 +22,7 @@ class JacksonFromDataEntryConverter( when (it) { is DataValue.StringDataValue -> objectMapper.readValue( it.value, - objectMapper.typeFactory.constructType(typeReference.getType()) + objectMapper.typeFactory.constructType(typeReference.getType()), ) else -> throw IllegalArgumentException("Only string DataEntry can be converted to TypeReference") } diff --git a/we-contract-sdk-test/src/main/kotlin/com/wavesenterprise/sdk/contract/test/data/TestDataProvider.kt b/we-contract-sdk-test/src/main/kotlin/com/wavesenterprise/sdk/contract/test/data/TestDataProvider.kt index 1af396e..eba13c8 100644 --- a/we-contract-sdk-test/src/main/kotlin/com/wavesenterprise/sdk/contract/test/data/TestDataProvider.kt +++ b/we-contract-sdk-test/src/main/kotlin/com/wavesenterprise/sdk/contract/test/data/TestDataProvider.kt @@ -48,7 +48,7 @@ class TestDataProvider private constructor() { timestamp = Timestamp.fromUtcTimestamp(Instant.now().toEpochMilli()), contractVersion = ContractVersion(0), feeAssetId = AssetId(randomBytesFromUUID()), - senderPublicKey = PublicKey(randomBytesFromUUID()) + senderPublicKey = PublicKey(randomBytesFromUUID()), ) TxType.CREATE_CONTRACT -> CreateContractTransaction( id = txId(), @@ -67,7 +67,7 @@ class TestDataProvider private constructor() { imageHash = ContractImageHash("imageHash"), ) else -> throw IllegalArgumentException( - "Only CALL_CONTRACT and CREATE_CONTRACT are allowed as transaction type" + "Only CALL_CONTRACT and CREATE_CONTRACT are allowed as transaction type", ) } } diff --git a/we-contract-sdk-test/src/main/kotlin/com/wavesenterprise/sdk/contract/test/state/ContractTestStateFactory.kt b/we-contract-sdk-test/src/main/kotlin/com/wavesenterprise/sdk/contract/test/state/ContractTestStateFactory.kt index e3440b8..46f6805 100644 --- a/we-contract-sdk-test/src/main/kotlin/com/wavesenterprise/sdk/contract/test/state/ContractTestStateFactory.kt +++ b/we-contract-sdk-test/src/main/kotlin/com/wavesenterprise/sdk/contract/test/state/ContractTestStateFactory.kt @@ -18,7 +18,7 @@ class ContractTestStateFactory private constructor() { DefaultBackingMapContractStateFactory( NoOpNodeValuesProvider, contractToDataValueConverter = JacksonContractToDataValueConverter(objectMapper), - contractFromDataEntryConverter = JacksonFromDataEntryConverter(objectMapper) + contractFromDataEntryConverter = JacksonFromDataEntryConverter(objectMapper), ).buildContractState(ContractId(txId())) } } From e2508fa9f7d8ecf463c4d990fbfb3345b75e8ae2 Mon Sep 17 00:00:00 2001 From: dgeorgiev Date: Fri, 28 Jun 2024 14:48:11 +0300 Subject: [PATCH 13/20] [WTCH-320] Upgrade jacoco plugin to 0.8.12 --- build.gradle.kts | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index e5f31d8..16b38f5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -301,6 +301,6 @@ configure( jacoco { toolVersion = jacocoToolVersion - reportsDirectory.set(file("$buildDir/jacocoReports")) + reportsDirectory.set(layout.buildDirectory.dir("jacocoReports").get().asFile) } } diff --git a/gradle.properties b/gradle.properties index cb6518d..67b1e2b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,7 +10,7 @@ gradleDependencyManagementVersion=1.1.5 detektVersion=1.23.6 gitPropertiesVersion=2.2.2 palantirGitVersion=0.12.2 -jacocoToolVersion=0.8.8 +jacocoToolVersion=0.8.12 jGitVerVersion=0.9.1 dokkaVersion=1.6.21 nexusStagingVersion=0.30.0 From 49ea5286cf372de662aaf01d72fa479b4159dbcc Mon Sep 17 00:00:00 2001 From: dgeorgiev Date: Fri, 28 Jun 2024 15:59:28 +0300 Subject: [PATCH 14/20] [WTCH-321] Upgrade com.palantir.git-version plugin to 3.1.0 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 67b1e2b..e593a4b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ org.gradle.jvmargs=-Xmx4g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 gradleDependencyManagementVersion=1.1.5 detektVersion=1.23.6 gitPropertiesVersion=2.2.2 -palantirGitVersion=0.12.2 +palantirGitVersion=3.1.0 jacocoToolVersion=0.8.12 jGitVerVersion=0.9.1 dokkaVersion=1.6.21 From cbeac904ee47fd78be4dabf536721467fb5adf01 Mon Sep 17 00:00:00 2001 From: dgeorgiev Date: Fri, 28 Jun 2024 16:09:42 +0300 Subject: [PATCH 15/20] [WTCH-322] Transition from io.codearte.nexus-staging to io.github.gradle-nexus.publish-plugin 3.1.0 --- .gitlab-ci.yml | 2 +- build.gradle.kts | 17 ++++++++++++----- gradle.properties | 2 +- settings.gradle.kts | 4 ++-- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 12b6a24..8eaf9c3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -63,7 +63,7 @@ tests&build_for_maven_central: script: - cat $we_maven_central_gpg | base64 --decode > "$(pwd)/we_maven_central.gpg" - ./gradlew --no-parallel -PsonaTypeMavenUser=$SONATYPE_USER -PsonaTypeMavenPassword=$SONATYPE_PASSWORD -Psigning.keyId=$SIGN_KEY_ID -Psigning.password=$SIGN_PASSWORD -Psigning.secretKeyRingFile="$(pwd)/we_maven_central.gpg" version check build publish - - ./gradlew -PsonaTypeMavenUser=$SONATYPE_USER -PsonaTypeMavenPassword=$SONATYPE_PASSWORD closeAndReleaseRepository + - ./gradlew -PsonaTypeMavenUser=$SONATYPE_USER -PsonaTypeMavenPassword=$SONATYPE_PASSWORD closeAndReleaseStagingRepository - ./samples/take_root_version_for_build.sh only: - master diff --git a/build.gradle.kts b/build.gradle.kts index 16b38f5..09eb278 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -42,7 +42,7 @@ plugins { kotlin("jvm") apply false `maven-publish` signing - id("io.codearte.nexus-staging") + id("io.github.gradle-nexus.publish-plugin") id("io.spring.dependency-management") apply false id("io.gitlab.arturbosch.detekt") id("com.palantir.git-version") apply false @@ -83,10 +83,17 @@ allprojects { } -nexusStaging { - serverUrl = "$sonaTypeBasePath/service/local/" - username = sonaTypeMavenUser - password = sonaTypeMavenPassword +if (sonaTypeMavenUser != null && sonaTypeMavenUser != null) { + nexusPublishing { + repositories { + sonatype { + nexusUrl.set(uri("$sonaTypeBasePath/service/local/")) + snapshotRepositoryUrl.set(uri("$sonaTypeBasePath/content/repositories/snapshots/")) + username.set(sonaTypeMavenUser) + password.set(sonaTypeMavenPassword) + } + } + } } subprojects { diff --git a/gradle.properties b/gradle.properties index e593a4b..a2d6034 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,7 +13,7 @@ palantirGitVersion=3.1.0 jacocoToolVersion=0.8.12 jGitVerVersion=0.9.1 dokkaVersion=1.6.21 -nexusStagingVersion=0.30.0 +nexusPublishVersion=2.0.0 # Core infrastructure libs versions kotlinVersion=1.9.23 diff --git a/settings.gradle.kts b/settings.gradle.kts index 709d3ee..f887f1b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -6,12 +6,12 @@ pluginManagement { val palantirGitVersion: String by settings val jGitVerVersion: String by settings val dokkaVersion: String by settings - val nexusStagingVersion: String by settings + val nexusPublishVersion: String by settings plugins { kotlin("jvm") version kotlinVersion apply false `maven-publish` - id("io.codearte.nexus-staging") version nexusStagingVersion apply false + id("io.github.gradle-nexus.publish-plugin") version nexusPublishVersion apply false id("io.spring.dependency-management") version gradleDependencyManagementVersion apply false id("io.gitlab.arturbosch.detekt") version detektVersion apply false id("com.palantir.git-version") version palantirGitVersion apply false From 7d3bfc0dd4464eaa3192f6c9d42f15f8cb88f24f Mon Sep 17 00:00:00 2001 From: dgeorgiev Date: Fri, 28 Jun 2024 16:25:43 +0300 Subject: [PATCH 16/20] [WTCH-323] Upgrade com.gorylenko.gradle-git-properties to 2.4.1 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index a2d6034..a6636f9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,7 +8,7 @@ org.gradle.jvmargs=-Xmx4g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 # Build plugins gradleDependencyManagementVersion=1.1.5 detektVersion=1.23.6 -gitPropertiesVersion=2.2.2 +gitPropertiesVersion=2.4.1 palantirGitVersion=3.1.0 jacocoToolVersion=0.8.12 jGitVerVersion=0.9.1 From 4cba61501df97a699054ee5827c8d4d7e411aa09 Mon Sep 17 00:00:00 2001 From: dgeorgiev Date: Fri, 28 Jun 2024 16:39:05 +0300 Subject: [PATCH 17/20] [WTCH-324] Upgrade org.jetbrains.dokka to 1.9.20 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index a6636f9..1fec695 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,7 +12,7 @@ gitPropertiesVersion=2.4.1 palantirGitVersion=3.1.0 jacocoToolVersion=0.8.12 jGitVerVersion=0.9.1 -dokkaVersion=1.6.21 +dokkaVersion=1.9.20 nexusPublishVersion=2.0.0 # Core infrastructure libs versions From f1e6be00f73979f550f7f0c048d5afbc0ecf1dce Mon Sep 17 00:00:00 2001 From: dgeorgiev Date: Fri, 28 Jun 2024 17:41:30 +0300 Subject: [PATCH 18/20] [WTCH-340] Upgrade we-node-client to version with actual tech stack --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 1fec695..83770f3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -32,7 +32,7 @@ junitVersion=5.10.2 mockkVersion=1.13.11 # WE SDK -weNodeClientVersion=1.4.1 +weNodeClientVersion=1.4.3-bc82bdbb-SNAPSHOT # GitHub properties gitHubProject = "waves-enterprise/we-contract-sdk" From 1af642be5bccab2d13dfad796ecbbe9f19046c7b Mon Sep 17 00:00:00 2001 From: dgeorgiev Date: Tue, 30 Jul 2024 10:11:17 +0000 Subject: [PATCH 19/20] [WTCH-339] Fix detekt issues --- build.gradle.kts | 9 ++++++- gradle/detekt-config.yml | 3 +-- .../contract/api/state/ContractStateReader.kt | 1 + .../state/{extensions.kt => Extensions.kt} | 0 .../sdk/contract/api/state/TypeReference.kt | 2 +- .../factory/ContractBlockingClientFactory.kt | 7 ++++- .../DefaultLocalContractValidatorImplTest.kt | 2 +- .../invocation/ParamsBuilderImplTest.kt | 8 +++--- .../ContractBlockingClientFactoryTest.kt | 7 ++--- .../client/invocation/util/ModelFactory.kt | 2 +- .../impl/DefaultLocalContractValidatorImpl.kt | 13 ++++++++-- .../core/dispatch/ContractDispatcher.kt | 3 +++ .../process/ContractHandlerFactoryImpl.kt | 2 +- .../process/ContractTransactionProcessor.kt | 1 + .../contract/core/state/ContractStateEx.kt | 8 +++--- .../core/state/ContractStateReaderIml.kt | 5 +++- .../process/ContractHandlerFactoryImplTest.kt | 26 +++++++++---------- .../ContractHandlerInvocationExtractorTest.kt | 24 ++++++++--------- ...actInvocationArgumentsExtractorImplTest.kt | 8 +++--- .../core/state/ContractStateExTest.kt | 8 +++--- .../contract/core/state/ContractStateTest.kt | 8 +++--- .../sdk/contract/core/utils/ModelFactory.kt | 4 +-- .../GrpcJacksonContractDispatcherBuilder.kt | 1 + .../connect/EnvGrpcConnectionProperties.kt | 3 ++- ...acksonContractDispatcherIntegrationTest.kt | 20 +++++++------- .../ContractFromDataEntryConverterTest.kt | 14 +++++----- .../jackson/JacksonFromDataEntryConverter.kt | 2 +- 27 files changed, 112 insertions(+), 79 deletions(-) rename we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/state/{extensions.kt => Extensions.kt} (100%) diff --git a/build.gradle.kts b/build.gradle.kts index 09eb278..2a284a9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -197,7 +197,14 @@ configure( exclude("resources/") exclude("build/") config.setFrom(detektConfigFilePath) - setSource(files("src/main/java", "src/main/kotlin")) + setSource( + files( + "src/main/java", + "src/test/java", + "src/main/kotlin", + "src/test/kotlin", + ) + ) } val sourcesJar by tasks.creating(Jar::class) { diff --git a/gradle/detekt-config.yml b/gradle/detekt-config.yml index 870799d..f73cbaa 100644 --- a/gradle/detekt-config.yml +++ b/gradle/detekt-config.yml @@ -1,6 +1,5 @@ build: - # TODO: https://jira.web3tech.ru/browse/WTCH-339 - maxIssues: 150 + maxIssues: 50 excludeCorrectable: false weights: # complexity: 2 diff --git a/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/state/ContractStateReader.kt b/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/state/ContractStateReader.kt index 7d47f9c..46e0053 100644 --- a/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/state/ContractStateReader.kt +++ b/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/state/ContractStateReader.kt @@ -4,6 +4,7 @@ import com.wavesenterprise.sdk.contract.api.state.mapping.ReadMapping import com.wavesenterprise.sdk.contract.api.wrc.WRC12Meta import java.util.Optional +@Suppress("TooManyFunctions") interface ContractStateReader { /** * Gets value by key diff --git a/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/state/extensions.kt b/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/state/Extensions.kt similarity index 100% rename from we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/state/extensions.kt rename to we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/state/Extensions.kt diff --git a/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/state/TypeReference.kt b/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/state/TypeReference.kt index 051a58a..c98f844 100644 --- a/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/state/TypeReference.kt +++ b/we-contract-sdk-api/src/main/kotlin/com/wavesenterprise/sdk/contract/api/state/TypeReference.kt @@ -3,7 +3,7 @@ package com.wavesenterprise.sdk.contract.api.state import java.lang.reflect.ParameterizedType import java.lang.reflect.Type -abstract class TypeReference() { +abstract class TypeReference { private val storedType: Type diff --git a/we-contract-sdk-client/we-contract-sdk-blocking-client/src/main/kotlin/com/wavesenterprise/sdk/contract/client/invocation/factory/ContractBlockingClientFactory.kt b/we-contract-sdk-client/we-contract-sdk-blocking-client/src/main/kotlin/com/wavesenterprise/sdk/contract/client/invocation/factory/ContractBlockingClientFactory.kt index 72b9679..786b53a 100644 --- a/we-contract-sdk-client/we-contract-sdk-blocking-client/src/main/kotlin/com/wavesenterprise/sdk/contract/client/invocation/factory/ContractBlockingClientFactory.kt +++ b/we-contract-sdk-client/we-contract-sdk-blocking-client/src/main/kotlin/com/wavesenterprise/sdk/contract/client/invocation/factory/ContractBlockingClientFactory.kt @@ -26,6 +26,7 @@ import com.wavesenterprise.sdk.node.domain.tx.ContractTx import com.wavesenterprise.sdk.tx.signer.TxSigner import java.lang.reflect.Proxy +@Suppress("LongParameterList") open class ContractBlockingClientFactory( private val contractClass: Class?, private val contractInterface: Class, @@ -52,7 +53,11 @@ open class ContractBlockingClientFactory( private val txTypeResolver: TxTypeResolver = TxTypeResolverImpl() private val invocationHandlerFactory = ContractHandlerInvocationHandlerFactory(paramsBuilder, txTypeResolver) - open fun executeContract(contractId: ContractId? = null, txSigner: TxSigner, receiver: (S) -> Unit): ExecutionContext { + open fun executeContract( + contractId: ContractId? = null, + txSigner: TxSigner, + receiver: (S) -> Unit, + ): ExecutionContext { return executionContext( contractId = contractId, txSigner = txSigner, diff --git a/we-contract-sdk-client/we-contract-sdk-blocking-client/src/test/kotlin/com/wavesenterprise/sdk/contract/client/invocation/DefaultLocalContractValidatorImplTest.kt b/we-contract-sdk-client/we-contract-sdk-blocking-client/src/test/kotlin/com/wavesenterprise/sdk/contract/client/invocation/DefaultLocalContractValidatorImplTest.kt index 28233f6..6f9ff47 100644 --- a/we-contract-sdk-client/we-contract-sdk-blocking-client/src/test/kotlin/com/wavesenterprise/sdk/contract/client/invocation/DefaultLocalContractValidatorImplTest.kt +++ b/we-contract-sdk-client/we-contract-sdk-blocking-client/src/test/kotlin/com/wavesenterprise/sdk/contract/client/invocation/DefaultLocalContractValidatorImplTest.kt @@ -96,7 +96,7 @@ internal class DefaultLocalContractValidatorImplTest { nodeContractStateValuesProvider.getForKey(any(), any()) } returnsMany listOf( Optional.empty(), - Optional.of(userDataEntry) + Optional.of(userDataEntry), ) assertDoesNotThrow { contractHandler.put(user) } diff --git a/we-contract-sdk-client/we-contract-sdk-blocking-client/src/test/kotlin/com/wavesenterprise/sdk/contract/client/invocation/ParamsBuilderImplTest.kt b/we-contract-sdk-client/we-contract-sdk-blocking-client/src/test/kotlin/com/wavesenterprise/sdk/contract/client/invocation/ParamsBuilderImplTest.kt index 1af9c0d..de0a78e 100644 --- a/we-contract-sdk-client/we-contract-sdk-blocking-client/src/test/kotlin/com/wavesenterprise/sdk/contract/client/invocation/ParamsBuilderImplTest.kt +++ b/we-contract-sdk-client/we-contract-sdk-blocking-client/src/test/kotlin/com/wavesenterprise/sdk/contract/client/invocation/ParamsBuilderImplTest.kt @@ -17,7 +17,7 @@ import kotlin.reflect.jvm.javaMethod internal class ParamsBuilderImplTest { private val contractToDataValueConverter: ContractToDataValueConverter = JacksonContractToDataValueConverter( - jacksonObjectMapper() + jacksonObjectMapper(), ) private val paramsBuilder = ParamsBuilderImpl(contractToDataValueConverter) @@ -40,9 +40,9 @@ internal class ParamsBuilderImplTest { assertEquals( DataEntry( key = DataKey(method.parameters[i - 1].getAnnotation(InvokeParam::class.java).name), - value = contractToDataValueConverter.convert(args[i - 1]) + value = contractToDataValueConverter.convert(args[i - 1]), ), - this + this, ) } } @@ -64,7 +64,7 @@ internal class ParamsBuilderImplTest { TestProcessor::testActionCallInteger.javaMethod!! to arrayOf(1), TestProcessor::testActionCallString.javaMethod!! to arrayOf("test"), TestProcessor::testActionCallWithoutParams.javaMethod!! to null, - TestProcessor::testActionCallWithSeveralParameters.javaMethod!! to arrayOf(TestObject(), 1, "str", true) + TestProcessor::testActionCallWithSeveralParameters.javaMethod!! to arrayOf(TestObject(), 1, "str", true), ) } } diff --git a/we-contract-sdk-client/we-contract-sdk-blocking-client/src/test/kotlin/com/wavesenterprise/sdk/contract/client/invocation/factory/ContractBlockingClientFactoryTest.kt b/we-contract-sdk-client/we-contract-sdk-blocking-client/src/test/kotlin/com/wavesenterprise/sdk/contract/client/invocation/factory/ContractBlockingClientFactoryTest.kt index 8aec768..64a3aba 100644 --- a/we-contract-sdk-client/we-contract-sdk-blocking-client/src/test/kotlin/com/wavesenterprise/sdk/contract/client/invocation/factory/ContractBlockingClientFactoryTest.kt +++ b/we-contract-sdk-client/we-contract-sdk-blocking-client/src/test/kotlin/com/wavesenterprise/sdk/contract/client/invocation/factory/ContractBlockingClientFactoryTest.kt @@ -58,7 +58,8 @@ class ContractBlockingClientFactoryTest { private val objectMapper = jacksonObjectMapper() - private val localValidationContextManager: LocalValidationContextManager = ThreadLocalLocalValidationContextManager() + private val localValidationContextManager: LocalValidationContextManager = + ThreadLocalLocalValidationContextManager() @MockK lateinit var txSigner: TxSigner @@ -116,7 +117,7 @@ class ContractBlockingClientFactoryTest { listOf( DataEntry(DataKey("action"), DataValue.StringDataValue("put")), userDataEntry, - ) + ), ) every { txSigner.sign(capture(signRequestCapture)) } returns callContractTx every { txService.broadcast(capture(txCaptor)) } returns callContractTx @@ -166,7 +167,7 @@ class ContractBlockingClientFactoryTest { listOf( DataEntry(DataKey("action"), DataValue.StringDataValue("createContract")), userDataEntry, - ) + ), ) every { txSigner.sign(capture(signRequestCapture)) } returns createContractTx diff --git a/we-contract-sdk-client/we-contract-sdk-blocking-client/src/test/kotlin/com/wavesenterprise/sdk/contract/client/invocation/util/ModelFactory.kt b/we-contract-sdk-client/we-contract-sdk-blocking-client/src/test/kotlin/com/wavesenterprise/sdk/contract/client/invocation/util/ModelFactory.kt index 81c6547..30822cb 100644 --- a/we-contract-sdk-client/we-contract-sdk-blocking-client/src/test/kotlin/com/wavesenterprise/sdk/contract/client/invocation/util/ModelFactory.kt +++ b/we-contract-sdk-client/we-contract-sdk-blocking-client/src/test/kotlin/com/wavesenterprise/sdk/contract/client/invocation/util/ModelFactory.kt @@ -76,5 +76,5 @@ fun createContractTx( version = TxVersion(1), image = ContractImage("image"), imageHash = ContractImageHash("imageHash"), - contractName = ContractName("contractName") + contractName = ContractName("contractName"), ) diff --git a/we-contract-sdk-client/we-contract-sdk-client-local-validator/src/main/kotlin/com/wavesenterprise/sdk/client/local/validator/impl/DefaultLocalContractValidatorImpl.kt b/we-contract-sdk-client/we-contract-sdk-client-local-validator/src/main/kotlin/com/wavesenterprise/sdk/client/local/validator/impl/DefaultLocalContractValidatorImpl.kt index 95e8088..bc90267 100644 --- a/we-contract-sdk-client/we-contract-sdk-client-local-validator/src/main/kotlin/com/wavesenterprise/sdk/client/local/validator/impl/DefaultLocalContractValidatorImpl.kt +++ b/we-contract-sdk-client/we-contract-sdk-client-local-validator/src/main/kotlin/com/wavesenterprise/sdk/client/local/validator/impl/DefaultLocalContractValidatorImpl.kt @@ -79,6 +79,7 @@ class DefaultLocalContractValidatorImpl( } } + @Suppress("SwallowedException", "SpreadOperator") private fun preValidate( method: Method, args: Array, @@ -89,7 +90,11 @@ class DefaultLocalContractValidatorImpl( method.invoke(implementation, *args) } catch (e: InvocationTargetException) { throw ContractPreValidationException( - message = "Error on prevalidation of contract invoke with params : \n" + " methodName : ${method.name} \n" + " implementation : $implementation \n" + " args : $args \n" + " contractId : ${contractId.asBase58String()}", + message = "Error on prevalidation of contract invoke with params : " + + "\n" + " methodName : ${method.name} " + + "\n" + " implementation : $implementation " + + "\n" + " args : $args " + + "\n" + " contractId : ${contractId.asBase58String()}", cause = e.targetException, method = method, implementation = implementation, @@ -98,7 +103,11 @@ class DefaultLocalContractValidatorImpl( ) } catch (e: IllegalAccessException) { throw ContractPreValidationException( - message = "Error on prevalidation of contract invoke with params : \n" + " methodName : ${method.name} \n" + " implementation : $implementation \n" + " args : $args \n" + " contractId : ${contractId.asBase58String()}", + message = "Error on prevalidation of contract invoke with params : " + + "\n" + " methodName : ${method.name} " + + "\n" + " implementation : $implementation " + + "\n" + " args : $args " + + "\n" + " contractId : ${contractId.asBase58String()}", cause = e, method = method, implementation = implementation, diff --git a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/dispatch/ContractDispatcher.kt b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/dispatch/ContractDispatcher.kt index c00c9ff..9660c40 100644 --- a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/dispatch/ContractDispatcher.kt +++ b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/dispatch/ContractDispatcher.kt @@ -1,3 +1,5 @@ +@file:Suppress("TooGenericExceptionCaught") + package com.wavesenterprise.sdk.contract.core.dispatch import com.wavesenterprise.sdk.contract.api.exception.RecoverableException @@ -22,6 +24,7 @@ import kotlin.system.exitProcess private const val ERROR_EXIT_CODE = 7 // extract interface - move to api +@Suppress("LongParameterList") class ContractDispatcher( private val connectContractService: ContractService, private val txContractService: ContractService, diff --git a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/process/ContractHandlerFactoryImpl.kt b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/process/ContractHandlerFactoryImpl.kt index e45acf0..9287604 100644 --- a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/process/ContractHandlerFactoryImpl.kt +++ b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/process/ContractHandlerFactoryImpl.kt @@ -24,7 +24,7 @@ class ContractHandlerFactoryImpl( return constructors.first().newInstanceFor(tx, contractState) } - @Suppress("UNCHECKED_CAST", "IMPLICIT_CAST_TO_ANY") + @Suppress("UNCHECKED_CAST", "IMPLICIT_CAST_TO_ANY", "SpreadOperator") private fun Constructor<*>.newInstanceFor( tx: ContractTransaction, contractState: ContractState, diff --git a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/process/ContractTransactionProcessor.kt b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/process/ContractTransactionProcessor.kt index 13f3e01..48e67a6 100644 --- a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/process/ContractTransactionProcessor.kt +++ b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/process/ContractTransactionProcessor.kt @@ -8,6 +8,7 @@ class ContractTransactionProcessor( private val contractHandlerFactory: ContractHandlerFactory<*>, ) { + @Suppress("SpreadOperator") fun process(contractTransaction: ContractTransaction) { val contractHandler = contractHandlerFactory.createHandler(contractTransaction) diff --git a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateEx.kt b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateEx.kt index 8af363e..645882a 100644 --- a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateEx.kt +++ b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateEx.kt @@ -29,12 +29,12 @@ inline operator fun ContractState.getValue( ): T { val type = object : TypeReference() {}.getType() val name = keyName(property.name) - if (type.isMapping()) { + return if (type.isMapping()) { require(!property.returnType.isMarkedNullable) { "Can not declare nullable mapping" } val mappingType = (type as ParameterizedType).actualTypeArguments[0] - return if (mappingType.isSimpleType()) { + if (mappingType.isSimpleType()) { getMapping(mappingType as Class<*>, name) as T } else { val typeRef = DynamicTypeReference(mappingType) @@ -42,14 +42,14 @@ inline operator fun ContractState.getValue( } } else { if (type.isSimpleType()) { - return if (property.returnType.isMarkedNullable) { + if (property.returnType.isMarkedNullable) { val opt = tryGet(name, type as Class<*>) as Optional<*> opt.orElse(null) as T } else { get(name, type as Class<*>) as T } } else { - return if (property.returnType.isMarkedNullable) { + if (property.returnType.isMarkedNullable) { val typeRef = DynamicTypeReference(type) val opt = tryGet(name, typeRef) as Optional<*> opt.orElse(null) as T diff --git a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateReaderIml.kt b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateReaderIml.kt index a924456..7f758a4 100644 --- a/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateReaderIml.kt +++ b/we-contract-sdk-core/src/main/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateReaderIml.kt @@ -16,6 +16,7 @@ import com.wavesenterprise.sdk.node.domain.contract.ContractId import com.wavesenterprise.sdk.node.exception.specific.DataKeyNotExistException import java.util.Optional +@Suppress("TooManyFunctions") class ContractStateReaderIml( private val contractId: ContractId, private val nodeContractStateValuesProvider: NodeContractStateValuesProvider, @@ -90,9 +91,11 @@ class ContractStateReaderIml( .associate { it.key to entryMappingFn(it.value) } } + @Suppress("SwallowedException") private fun getKeyValue(key: String): DataEntry? = try { - backingMap[key] ?: nodeContractStateValuesProvider.getForKeyAsNullable(contractId, key)?.also { backingMap[key] = it } + backingMap[key] ?: nodeContractStateValuesProvider + .getForKeyAsNullable(contractId, key)?.also { backingMap[key] = it } } catch (ex: DataKeyNotExistException) { null } diff --git a/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/process/ContractHandlerFactoryImplTest.kt b/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/process/ContractHandlerFactoryImplTest.kt index 5e02e71..18027f1 100644 --- a/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/process/ContractHandlerFactoryImplTest.kt +++ b/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/process/ContractHandlerFactoryImplTest.kt @@ -48,7 +48,7 @@ internal class ContractHandlerFactoryImplTest { fun `should create contract handler with ContractTransaction`() { val contractHandlerFactory = ContractHandlerFactoryImpl( contractState, - TestContractHandlerForContractTransaction::class.java + TestContractHandlerForContractTransaction::class.java, ) val handlerForState = contractHandlerFactory.createHandler(contractTransaction) @@ -60,7 +60,7 @@ internal class ContractHandlerFactoryImplTest { fun `should create contract handler with ContractState`() { val contractHandlerFactory = ContractHandlerFactoryImpl( contractState, - TestContractHandlerForState::class.java + TestContractHandlerForState::class.java, ) val handlerForState = contractHandlerFactory.createHandler(contractTransaction) @@ -72,7 +72,7 @@ internal class ContractHandlerFactoryImplTest { fun `should create contract handler with ContractCall and ContractState`() { val contractHandlerFactory = ContractHandlerFactoryImpl( contractState, - TestContractHandlerForContractCallAndState::class.java + TestContractHandlerForContractCallAndState::class.java, ) val handlerForStateAndCall = contractHandlerFactory.createHandler(contractTransaction) @@ -85,7 +85,7 @@ internal class ContractHandlerFactoryImplTest { fun `should create contract handler with ContractTransaction and ContractState`() { val contractHandlerFactory = ContractHandlerFactoryImpl( contractState, - TestContractHandlerForContractTransactionAndState::class.java + TestContractHandlerForContractTransactionAndState::class.java, ) val handlerForTransactionAndCall = contractHandlerFactory.createHandler(contractTransaction) @@ -98,7 +98,7 @@ internal class ContractHandlerFactoryImplTest { fun `should create contract handler with ContractCall`() { val contractHandlerFactory = ContractHandlerFactoryImpl( contractState, - TestContractHandlerForContractCall::class.java + TestContractHandlerForContractCall::class.java, ) val handlerForCall = contractHandlerFactory.createHandler(contractTransaction) @@ -111,7 +111,7 @@ internal class ContractHandlerFactoryImplTest { val illegalArgumentException = assertThrows { val contractHandlerFactory = ContractHandlerFactoryImpl( contractState, - TestContractHandlerWithSecondConstructor::class.java + TestContractHandlerWithSecondConstructor::class.java, ) contractHandlerFactory.createHandler(contractTransaction) } @@ -119,7 +119,7 @@ internal class ContractHandlerFactoryImplTest { assertEquals( "ContractHandler ${TestContractHandlerWithSecondConstructor::class.java.canonicalName} " + "class should have exactly one public constructor", - message + message, ) } } @@ -130,7 +130,7 @@ internal class ContractHandlerFactoryImplTest { val txIdCaptor = slot() val contractHandlerFactory = ContractHandlerFactoryImpl( contractState, - TestContractHandlerForContractTransactionAndState::class.java + TestContractHandlerForContractTransactionAndState::class.java, ) every { contractTransaction.type } returns TxType.CREATE_CONTRACT every { contractState.put(CONTRACT_ID_KEY, capture(txIdCaptor)) } returns contractState @@ -140,8 +140,8 @@ internal class ContractHandlerFactoryImplTest { assertTrue( wrc12MetaCaptor.captured.impls.contains( - TestContractHandlerForContractTransactionAndState::class.java.name - ) + TestContractHandlerForContractTransactionAndState::class.java.name, + ), ) assertEquals(contractTransaction.id, txIdCaptor.captured) } @@ -151,7 +151,7 @@ internal class ContractHandlerFactoryImplTest { val wrc12MetaCaptor = slot() val contractHandlerFactory = ContractHandlerFactoryImpl( contractState, - TestContractHandlerForContractTransactionAndState::class.java + TestContractHandlerForContractTransactionAndState::class.java, ) every { contractTransaction.type } returns TxType.CALL_CONTRACT every { contractState.put(CONTRACT_META_KEY, capture(wrc12MetaCaptor)) } returns contractState @@ -160,8 +160,8 @@ internal class ContractHandlerFactoryImplTest { assertTrue( wrc12MetaCaptor.captured.impls.contains( - TestContractHandlerForContractTransactionAndState::class.java.name - ) + TestContractHandlerForContractTransactionAndState::class.java.name, + ), ) } diff --git a/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/process/ContractHandlerInvocationExtractorTest.kt b/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/process/ContractHandlerInvocationExtractorTest.kt index 438036b..3260051 100644 --- a/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/process/ContractHandlerInvocationExtractorTest.kt +++ b/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/process/ContractHandlerInvocationExtractorTest.kt @@ -34,8 +34,8 @@ internal class ContractHandlerInvocationExtractorTest { } returns listOf( DataEntry( key = DataKey("action"), - value = DataValue.StringDataValue("init") - ) + value = DataValue.StringDataValue("init"), + ), ) val invocationExtractor = ContractHandlerInvocationExtractor(TestContractHandler::class.java) @@ -50,8 +50,8 @@ internal class ContractHandlerInvocationExtractorTest { } returns listOf( DataEntry( key = DataKey("action"), - value = DataValue.StringDataValue("createContract") - ) + value = DataValue.StringDataValue("createContract"), + ), ) val invocationExtractor = ContractHandlerInvocationExtractor(ContractHandlerWithInterface::class.java) @@ -66,8 +66,8 @@ internal class ContractHandlerInvocationExtractorTest { } returns listOf( DataEntry( key = DataKey("action"), - value = DataValue.StringDataValue("someOtherInitName") - ) + value = DataValue.StringDataValue("someOtherInitName"), + ), ) val invocationExtractor = ContractHandlerInvocationExtractor(TestContractHandler::class.java) @@ -82,11 +82,11 @@ internal class ContractHandlerInvocationExtractorTest { } returns listOf( DataEntry( key = DataKey("action"), - value = DataValue.StringDataValue("contractAction") + value = DataValue.StringDataValue("contractAction"), ), DataEntry( key = DataKey("param"), - value = DataValue.StringDataValue("blaValue") + value = DataValue.StringDataValue("blaValue"), ), ) @@ -102,11 +102,11 @@ internal class ContractHandlerInvocationExtractorTest { } returns listOf( DataEntry( key = DataKey("action"), - value = DataValue.StringDataValue("someOtherActionName") + value = DataValue.StringDataValue("someOtherActionName"), ), DataEntry( key = DataKey("param"), - value = DataValue.StringDataValue("blaValue") + value = DataValue.StringDataValue("blaValue"), ), ) @@ -123,7 +123,7 @@ internal class ContractHandlerInvocationExtractorTest { } returns listOf( DataEntry( key = DataKey("action"), - value = DataValue.StringDataValue(actionName) + value = DataValue.StringDataValue(actionName), ), ) val invocationExtractor = ContractHandlerInvocationExtractor(TestContractHandler::class.java) @@ -134,7 +134,7 @@ internal class ContractHandlerInvocationExtractorTest { assertEquals( "ContractAction named $actionName hasn't been found in class " + "com.wavesenterprise.sdk.contract.core.process.TestContractHandler", - message + message, ) } } diff --git a/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/process/ContractInvocationArgumentsExtractorImplTest.kt b/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/process/ContractInvocationArgumentsExtractorImplTest.kt index fdfb108..dcefa42 100644 --- a/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/process/ContractInvocationArgumentsExtractorImplTest.kt +++ b/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/process/ContractInvocationArgumentsExtractorImplTest.kt @@ -34,19 +34,19 @@ internal class ContractInvocationArgumentsExtractorImplTest { val booleanValue = false val domainArg = DataEntry( key = DataKey("someDomainDto"), - value = DataValue.StringDataValue("bla-bla") + value = DataValue.StringDataValue("bla-bla"), ) val longArg = DataEntry( key = DataKey("longParam"), - value = DataValue.IntegerDataValue(1L) + value = DataValue.IntegerDataValue(1L), ) val stringArg = DataEntry( key = DataKey("otherStringParamName"), - value = DataValue.StringDataValue("abc") + value = DataValue.StringDataValue("abc"), ) val booleanArg = DataEntry( key = DataKey("boolParam"), - value = DataValue.BooleanDataValue(booleanValue) + value = DataValue.BooleanDataValue(booleanValue), ) every { contractFromDataEntryConverter.convert(longArg, Long::class.java) diff --git a/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateExTest.kt b/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateExTest.kt index 6ffd984..d5f5b02 100644 --- a/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateExTest.kt +++ b/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateExTest.kt @@ -200,21 +200,23 @@ internal class ContractStateExTest { key = DataKey(key), value = converter.convert(it1), ) - } else null + } else { + null + }, ) } } return DefaultBackingMapContractStateFactory( nodeContractStateValuesProvider = nodeContractStateValuesProvider, contractToDataValueConverter = JacksonContractToDataValueConverter(objectMapper), - contractFromDataEntryConverter = JacksonFromDataEntryConverter(objectMapper) + contractFromDataEntryConverter = JacksonFromDataEntryConverter(objectMapper), ).buildContractState(ContractId(TestDataProvider.txId())) } private fun ContractState.result(key: String) = results()[0] data class User( - val name: String + val name: String, ) } diff --git a/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateTest.kt b/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateTest.kt index eaf2d6d..f4e9689 100644 --- a/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateTest.kt +++ b/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/state/ContractStateTest.kt @@ -87,7 +87,7 @@ internal class ContractStateTest { assertEquals(value, someDomainObject) assertEquals( value.toString(), - (contractState.results().find { it.key.value == key }?.value as DataValue.StringDataValue).value + (contractState.results().find { it.key.value == key }?.value as DataValue.StringDataValue).value, ) verify { contractToDataValueConverter.convert(someDomainObj) @@ -106,8 +106,8 @@ internal class ContractStateTest { } returns Optional.of( DataEntry( DataKey(readKey), - DataValue.StringDataValue.fromString(stateValue) - ) + DataValue.StringDataValue.fromString(stateValue), + ), ) every { contractToDataValueConverter.convert(any()) @@ -174,7 +174,7 @@ internal class ContractStateTest { }.apply { assertEquals( "Contract key length should be less than $MAX_KEY_LENGTH. Actual key value was '$longKey'", - message + message, ) } } diff --git a/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/utils/ModelFactory.kt b/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/utils/ModelFactory.kt index cdd3bed..4e460a7 100644 --- a/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/utils/ModelFactory.kt +++ b/we-contract-sdk-core/src/test/kotlin/com/wavesenterprise/sdk/contract/core/utils/ModelFactory.kt @@ -8,9 +8,9 @@ import com.wavesenterprise.sdk.node.domain.contract.ContractId import java.util.UUID fun contractId( - txId: TxId = txId() + txId: TxId = txId(), ) = ContractId( - txId = txId + txId = txId, ) fun txId( diff --git a/we-contract-sdk-grpc/src/main/kotlin/com/wavesenterprise/sdk/contract/grpc/GrpcJacksonContractDispatcherBuilder.kt b/we-contract-sdk-grpc/src/main/kotlin/com/wavesenterprise/sdk/contract/grpc/GrpcJacksonContractDispatcherBuilder.kt index 6855fc4..36373f3 100644 --- a/we-contract-sdk-grpc/src/main/kotlin/com/wavesenterprise/sdk/contract/grpc/GrpcJacksonContractDispatcherBuilder.kt +++ b/we-contract-sdk-grpc/src/main/kotlin/com/wavesenterprise/sdk/contract/grpc/GrpcJacksonContractDispatcherBuilder.kt @@ -25,6 +25,7 @@ import org.slf4j.LoggerFactory import java.util.concurrent.Executor import java.util.concurrent.Executors +@Suppress("TooManyFunctions") class GrpcJacksonContractDispatcherBuilder { private var contractHandlerType: Class<*>? = null diff --git a/we-contract-sdk-grpc/src/main/kotlin/com/wavesenterprise/sdk/contract/grpc/connect/EnvGrpcConnectionProperties.kt b/we-contract-sdk-grpc/src/main/kotlin/com/wavesenterprise/sdk/contract/grpc/connect/EnvGrpcConnectionProperties.kt index 950d01e..98e0070 100644 --- a/we-contract-sdk-grpc/src/main/kotlin/com/wavesenterprise/sdk/contract/grpc/connect/EnvGrpcConnectionProperties.kt +++ b/we-contract-sdk-grpc/src/main/kotlin/com/wavesenterprise/sdk/contract/grpc/connect/EnvGrpcConnectionProperties.kt @@ -7,11 +7,12 @@ class EnvGrpcConnectionProperties : GrpcConnectionProperties { override val nodeHost: String override val nodePort: Int override val connectionId: String - override val keepAliveSeconds: Long = 10L + override val keepAliveSeconds: Long = DEFAULT_KEEP_ALIVE_SECONDS override val authToken: String companion object { private val logger: Logger = LoggerFactory.getLogger(EnvGrpcConnectionProperties::class.java) + private const val DEFAULT_KEEP_ALIVE_SECONDS = 10L } init { diff --git a/we-contract-sdk-grpc/src/test/kotlin/com/wavesenterprise/sdk/contract/grpc/GrpcJacksonContractDispatcherIntegrationTest.kt b/we-contract-sdk-grpc/src/test/kotlin/com/wavesenterprise/sdk/contract/grpc/GrpcJacksonContractDispatcherIntegrationTest.kt index cac39e0..12f0e0f 100644 --- a/we-contract-sdk-grpc/src/test/kotlin/com/wavesenterprise/sdk/contract/grpc/GrpcJacksonContractDispatcherIntegrationTest.kt +++ b/we-contract-sdk-grpc/src/test/kotlin/com/wavesenterprise/sdk/contract/grpc/GrpcJacksonContractDispatcherIntegrationTest.kt @@ -64,11 +64,11 @@ internal class GrpcJacksonContractDispatcherIntegrationTest { params = listOf( DataEntry( key = DataKey("action"), - value = DataValue.StringDataValue("createContract") + value = DataValue.StringDataValue("createContract"), ), DataEntry( key = DataKey("createContract"), - value = DataValue.StringDataValue("initialValue") + value = DataValue.StringDataValue("initialValue"), ), ), fee = mockk(), @@ -78,8 +78,8 @@ internal class GrpcJacksonContractDispatcherIntegrationTest { feeAssetId = mockk(), image = mockk(), imageHash = mockk(), - contractName = ContractName("int test") - ) + contractName = ContractName("int test"), + ), ) val dispatcher = GrpcJacksonContractDispatcherBuilder.builder() @@ -107,11 +107,11 @@ internal class GrpcJacksonContractDispatcherIntegrationTest { params = listOf( DataEntry( key = DataKey("action"), - value = DataValue.StringDataValue("doSomeAction") + value = DataValue.StringDataValue("doSomeAction"), ), DataEntry( key = DataKey("createContract"), - value = DataValue.StringDataValue("initialValue") + value = DataValue.StringDataValue("initialValue"), ), ), fee = mockk(), @@ -119,8 +119,8 @@ internal class GrpcJacksonContractDispatcherIntegrationTest { proof = mockk(), timestamp = mockk().also { every { it.utcTimestampMillis } returns 1L }, feeAssetId = mockk(), - contractVersion = ContractVersion(1) - ) + contractVersion = ContractVersion(1), + ), ) every { txContractService.getContractKey(any()) @@ -192,8 +192,8 @@ class IntTestContractHandler( dtoId, listOf( MySampleContractDto("john", 18), - MySampleContractDto("harry", 54) - ) + MySampleContractDto("harry", 54), + ), ) } diff --git a/we-contract-sdk-grpc/src/test/kotlin/com/wavesenterprise/sdk/contract/grpc/jackson/ContractFromDataEntryConverterTest.kt b/we-contract-sdk-grpc/src/test/kotlin/com/wavesenterprise/sdk/contract/grpc/jackson/ContractFromDataEntryConverterTest.kt index 2d5360c..e0ce4d9 100644 --- a/we-contract-sdk-grpc/src/test/kotlin/com/wavesenterprise/sdk/contract/grpc/jackson/ContractFromDataEntryConverterTest.kt +++ b/we-contract-sdk-grpc/src/test/kotlin/com/wavesenterprise/sdk/contract/grpc/jackson/ContractFromDataEntryConverterTest.kt @@ -25,8 +25,8 @@ internal class ContractFromDataEntryConverterTest { value = DataValue.StringDataValue( """ { "name": "john", "age": 33 } - """.trimIndent() - ) + """.trimIndent(), + ), ) val result = converter.convert(dataEntry, SimpleDto::class.java) @@ -44,8 +44,8 @@ internal class ContractFromDataEntryConverterTest { value = DataValue.StringDataValue( """ [{ "name": "john", "age": 33 }, { "name": "jane", "age": 33 }] - """.trimIndent() - ) + """.trimIndent(), + ), ) val result = converter.convert(dataEntry, object : TypeReference>() {}) @@ -63,7 +63,7 @@ internal class ContractFromDataEntryConverterTest { val value = 123L val dataEntry = DataEntry( key = DataKey("someKey"), - value = DataValue.IntegerDataValue(value) + value = DataValue.IntegerDataValue(value), ) val expectedValue = when (integerClass) { Long::class.java -> value @@ -82,7 +82,7 @@ internal class ContractFromDataEntryConverterTest { val value = "strValue" val dataEntry = DataEntry( key = DataKey("someKey"), - value = DataValue.StringDataValue(value) + value = DataValue.StringDataValue(value), ) val result = converter.convert(dataEntry, String::class.java) @@ -95,7 +95,7 @@ internal class ContractFromDataEntryConverterTest { val value = "1.44" val dataEntry = DataEntry( key = DataKey("someKey"), - value = DataValue.StringDataValue(value) + value = DataValue.StringDataValue(value), ) val result = converter.convert(dataEntry, BigDecimal::class.java) diff --git a/we-contract-sdk-jackson/src/main/kotlin/com/wavesenterprise/sdk/contract/jackson/JacksonFromDataEntryConverter.kt b/we-contract-sdk-jackson/src/main/kotlin/com/wavesenterprise/sdk/contract/jackson/JacksonFromDataEntryConverter.kt index c1b583b..45f8c4c 100644 --- a/we-contract-sdk-jackson/src/main/kotlin/com/wavesenterprise/sdk/contract/jackson/JacksonFromDataEntryConverter.kt +++ b/we-contract-sdk-jackson/src/main/kotlin/com/wavesenterprise/sdk/contract/jackson/JacksonFromDataEntryConverter.kt @@ -53,7 +53,7 @@ class JacksonFromDataEntryConverter( Integer::class.java -> dataEntryValue.value.toInt() as T java.lang.Long::class.java -> dataEntryValue.value as T else -> { - throw IllegalStateException("Unknown valueType = '$valueType'. See on supported types: $supportedTypes") + error("Unknown valueType = '$valueType'. See on supported types: $supportedTypes") } } } From 70d3de10a22ed1963dc52a63ee561762cadb03f2 Mon Sep 17 00:00:00 2001 From: dgeorgiev Date: Wed, 31 Jul 2024 10:46:57 +0300 Subject: [PATCH 20/20] [WTCH-347] Upgrade we-node-client to 2.0.0 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 83770f3..3c9a30d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -32,7 +32,7 @@ junitVersion=5.10.2 mockkVersion=1.13.11 # WE SDK -weNodeClientVersion=1.4.3-bc82bdbb-SNAPSHOT +weNodeClientVersion=2.0.0 # GitHub properties gitHubProject = "waves-enterprise/we-contract-sdk"