From 74996848e29575cadcaaca89d053677d598cfff3 Mon Sep 17 00:00:00 2001 From: Mattheo Ioannou Date: Thu, 25 Feb 2021 15:09:50 +0100 Subject: [PATCH 1/7] add iterables class + tests --- iterables/build.gradle | 12 +++++ .../java/com/palantir/logsafe/Iterables.java | 42 +++++++++++++++++ .../com/palantir/logsafe/IterablesTest.java | 46 +++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 iterables/build.gradle create mode 100755 iterables/src/main/java/com/palantir/logsafe/Iterables.java create mode 100644 iterables/src/test/java/com/palantir/logsafe/IterablesTest.java diff --git a/iterables/build.gradle b/iterables/build.gradle new file mode 100644 index 00000000..57a3845c --- /dev/null +++ b/iterables/build.gradle @@ -0,0 +1,12 @@ +apply from: "${rootDir}/gradle/publish-jar.gradle" +apply plugin: 'com.palantir.revapi' + +dependencies { + api project(':safe-logging') + compile 'preconditions' + compileOnly 'com.google.code.findbugs:jsr305' + api 'com.google.errorprone:error_prone_annotations' + + testImplementation 'junit:junit' + testImplementation 'org.assertj:assertj-core' +} diff --git a/iterables/src/main/java/com/palantir/logsafe/Iterables.java b/iterables/src/main/java/com/palantir/logsafe/Iterables.java new file mode 100755 index 00000000..82748a82 --- /dev/null +++ b/iterables/src/main/java/com/palantir/logsafe/Iterables.java @@ -0,0 +1,42 @@ +/* + * (c) Copyright 2018 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package com.palantir.logsafe; + +import com.google.common.collect.Iterators; +import java.util.Collection; + +public final class Iterables { + private Iterables() {} + + public static > T getOnlyElement(C collection, Arg... args) { + Preconditions.checkState(collection.size() == 1, "Expected exactly one element in collection", args); + return Iterators.getOnlyElement(collection.iterator()); + } +} diff --git a/iterables/src/test/java/com/palantir/logsafe/IterablesTest.java b/iterables/src/test/java/com/palantir/logsafe/IterablesTest.java new file mode 100644 index 00000000..1ede83e1 --- /dev/null +++ b/iterables/src/test/java/com/palantir/logsafe/IterablesTest.java @@ -0,0 +1,46 @@ +/* + * (c) Copyright 2021 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.palantir.logsafe; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.google.common.collect.ImmutableSet; +import com.palantir.logsafe.exceptions.SafeIllegalStateException; +import org.junit.Test; + +public class IterablesTest { + @Test + public void getOnlyElementReturnsElement() { + String element = "element"; + assertThat(Iterables.getOnlyElement(ImmutableSet.of(element))).isEqualTo(element); + } + + @Test + public void getOnlyElementThrowsIfMoreThanOneElement() { + String element1 = "element1"; + String element2 = "element2"; + assertThatThrownBy(() -> Iterables.getOnlyElement(ImmutableSet.of(element1, element2))) + .isInstanceOf(SafeIllegalStateException.class); + } + + @Test + public void getOnlyElementThrowsIfEmpty() { + assertThatThrownBy(() -> Iterables.getOnlyElement(ImmutableSet.of())) + .isInstanceOf(SafeIllegalStateException.class); + } +} From ebcba89185ac6c37db5db86bec1de278cf4f60b1 Mon Sep 17 00:00:00 2001 From: Mattheo Ioannou Date: Thu, 25 Feb 2021 15:21:13 +0100 Subject: [PATCH 2/7] add option with custom message --- .../java/com/palantir/logsafe/Iterables.java | 11 +++++++- .../com/palantir/logsafe/IterablesTest.java | 25 ++++++++++++++++--- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/iterables/src/main/java/com/palantir/logsafe/Iterables.java b/iterables/src/main/java/com/palantir/logsafe/Iterables.java index 82748a82..b023bb16 100755 --- a/iterables/src/main/java/com/palantir/logsafe/Iterables.java +++ b/iterables/src/main/java/com/palantir/logsafe/Iterables.java @@ -29,14 +29,23 @@ */ package com.palantir.logsafe; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; import com.google.common.collect.Iterators; import java.util.Collection; public final class Iterables { + @VisibleForTesting + static final String DEFAULT_ERROR_MESSAGE = "Expected exactly one element in collection"; + private Iterables() {} public static > T getOnlyElement(C collection, Arg... args) { - Preconditions.checkState(collection.size() == 1, "Expected exactly one element in collection", args); + return getOnlyElement(collection, DEFAULT_ERROR_MESSAGE, args); + } + + public static > T getOnlyElement(C collection, String message, Arg... args) { + Preconditions.checkState(collection.size() == 1, message, args); return Iterators.getOnlyElement(collection.iterator()); } } diff --git a/iterables/src/test/java/com/palantir/logsafe/IterablesTest.java b/iterables/src/test/java/com/palantir/logsafe/IterablesTest.java index 1ede83e1..e85d5f6c 100644 --- a/iterables/src/test/java/com/palantir/logsafe/IterablesTest.java +++ b/iterables/src/test/java/com/palantir/logsafe/IterablesTest.java @@ -20,7 +20,6 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.google.common.collect.ImmutableSet; -import com.palantir.logsafe.exceptions.SafeIllegalStateException; import org.junit.Test; public class IterablesTest { @@ -35,12 +34,32 @@ public void getOnlyElementThrowsIfMoreThanOneElement() { String element1 = "element1"; String element2 = "element2"; assertThatThrownBy(() -> Iterables.getOnlyElement(ImmutableSet.of(element1, element2))) - .isInstanceOf(SafeIllegalStateException.class); + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining(Iterables.DEFAULT_ERROR_MESSAGE); } @Test public void getOnlyElementThrowsIfEmpty() { assertThatThrownBy(() -> Iterables.getOnlyElement(ImmutableSet.of())) - .isInstanceOf(SafeIllegalStateException.class); + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining(Iterables.DEFAULT_ERROR_MESSAGE); + } + + @Test + public void getOnlyElementThrowsIfMoreThanOneElementWithCustomMessage() { + String element1 = "element1"; + String element2 = "element2"; + String message = "some other error message"; + assertThatThrownBy(() -> Iterables.getOnlyElement(ImmutableSet.of(element1, element2), message)) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining(message); + } + + @Test + public void getOnlyElementThrowsIfEmptyWithCustomMessage() { + String message = "some other error message"; + assertThatThrownBy(() -> Iterables.getOnlyElement(ImmutableSet.of(), message)) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining(message); } } From 991c98d9ad5006fbdbc087dac9a11f240bab8ce8 Mon Sep 17 00:00:00 2001 From: Mattheo Ioannou Date: Thu, 25 Feb 2021 14:21:13 +0000 Subject: [PATCH 3/7] Add generated changelog entries --- changelog/@unreleased/pr-520.v2.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelog/@unreleased/pr-520.v2.yml diff --git a/changelog/@unreleased/pr-520.v2.yml b/changelog/@unreleased/pr-520.v2.yml new file mode 100644 index 00000000..2f9441a1 --- /dev/null +++ b/changelog/@unreleased/pr-520.v2.yml @@ -0,0 +1,5 @@ +type: improvement +improvement: + description: Add iterables package containing helper method for getting the only element in a collection + links: + - https://github.com/palantir/safe-logging/pull/520 From f53d83e6f95cb3a0f47d458d5414d1584a6a0e27 Mon Sep 17 00:00:00 2001 From: Mattheo Ioannou Date: Thu, 25 Feb 2021 15:54:06 +0100 Subject: [PATCH 4/7] release own package --- iterables/build.gradle | 4 ++-- .../java/com/palantir/logsafe/Iterables.java | 10 ++------ .../com/palantir/logsafe/IterablesTest.java | 24 +++---------------- settings.gradle | 1 + versions.lock | 12 +++++----- 5 files changed, 14 insertions(+), 37 deletions(-) diff --git a/iterables/build.gradle b/iterables/build.gradle index 57a3845c..1fb857b8 100644 --- a/iterables/build.gradle +++ b/iterables/build.gradle @@ -3,9 +3,9 @@ apply plugin: 'com.palantir.revapi' dependencies { api project(':safe-logging') - compile 'preconditions' + api project(':preconditions') compileOnly 'com.google.code.findbugs:jsr305' - api 'com.google.errorprone:error_prone_annotations' + compile group: 'com.google.guava', name: 'guava' testImplementation 'junit:junit' testImplementation 'org.assertj:assertj-core' diff --git a/iterables/src/main/java/com/palantir/logsafe/Iterables.java b/iterables/src/main/java/com/palantir/logsafe/Iterables.java index b023bb16..eff6858a 100755 --- a/iterables/src/main/java/com/palantir/logsafe/Iterables.java +++ b/iterables/src/main/java/com/palantir/logsafe/Iterables.java @@ -29,23 +29,17 @@ */ package com.palantir.logsafe; -import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.Iterators; import java.util.Collection; public final class Iterables { - @VisibleForTesting - static final String DEFAULT_ERROR_MESSAGE = "Expected exactly one element in collection"; + public static final String DEFAULT_ERROR_MESSAGE = "Expected exactly one element in collection"; private Iterables() {} public static > T getOnlyElement(C collection, Arg... args) { - return getOnlyElement(collection, DEFAULT_ERROR_MESSAGE, args); - } - - public static > T getOnlyElement(C collection, String message, Arg... args) { - Preconditions.checkState(collection.size() == 1, message, args); + Preconditions.checkState(collection.size() == 1, DEFAULT_ERROR_MESSAGE, args); return Iterators.getOnlyElement(collection.iterator()); } } diff --git a/iterables/src/test/java/com/palantir/logsafe/IterablesTest.java b/iterables/src/test/java/com/palantir/logsafe/IterablesTest.java index e85d5f6c..200a6b62 100644 --- a/iterables/src/test/java/com/palantir/logsafe/IterablesTest.java +++ b/iterables/src/test/java/com/palantir/logsafe/IterablesTest.java @@ -16,12 +16,12 @@ package com.palantir.logsafe; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - import com.google.common.collect.ImmutableSet; import org.junit.Test; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + public class IterablesTest { @Test public void getOnlyElementReturnsElement() { @@ -44,22 +44,4 @@ public void getOnlyElementThrowsIfEmpty() { .isInstanceOf(IllegalStateException.class) .hasMessageContaining(Iterables.DEFAULT_ERROR_MESSAGE); } - - @Test - public void getOnlyElementThrowsIfMoreThanOneElementWithCustomMessage() { - String element1 = "element1"; - String element2 = "element2"; - String message = "some other error message"; - assertThatThrownBy(() -> Iterables.getOnlyElement(ImmutableSet.of(element1, element2), message)) - .isInstanceOf(IllegalStateException.class) - .hasMessageContaining(message); - } - - @Test - public void getOnlyElementThrowsIfEmptyWithCustomMessage() { - String message = "some other error message"; - assertThatThrownBy(() -> Iterables.getOnlyElement(ImmutableSet.of(), message)) - .isInstanceOf(IllegalStateException.class) - .hasMessageContaining(message); - } } diff --git a/settings.gradle b/settings.gradle index 37098e47..f975b057 100644 --- a/settings.gradle +++ b/settings.gradle @@ -4,4 +4,5 @@ include 'safe-logging' include 'safe-logging-refactorings' include 'preconditions' include 'preconditions-assertj' +include 'iterables' diff --git a/versions.lock b/versions.lock index 137b1f5d..1cabf831 100644 --- a/versions.lock +++ b/versions.lock @@ -1,6 +1,12 @@ # Run ./gradlew --write-locks to regenerate this file com.google.code.findbugs:jsr305:3.0.2 (4 constraints: e932ae18) com.google.errorprone:error_prone_annotations:2.3.3 (4 constraints: f532671c) +com.google.guava:failureaccess:1.0.1 (1 constraints: 140ae1b4) +com.google.guava:guava:27.0.1-jre (4 constraints: 613b78c4) +com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava (1 constraints: bd17c918) +com.google.j2objc:j2objc-annotations:1.1 (1 constraints: b609eba0) +org.checkerframework:checker-qual:2.5.3 (4 constraints: 202a1965) +org.codehaus.mojo:animal-sniffer-annotations:1.17 (1 constraints: ed09d8aa) [Test dependencies] com.github.kevinstern:software-and-algorithms:1.0 (1 constraints: 7e12fcf5) @@ -13,17 +19,11 @@ com.google.errorprone:error_prone_core:2.3.3 (1 constraints: a8121509) com.google.errorprone:error_prone_refaster:2.3.3 (1 constraints: 0a050136) com.google.errorprone:error_prone_type_annotations:2.3.3 (1 constraints: f51029b8) com.google.errorprone:javac:9+181-r4173-1 (3 constraints: 003cc23b) -com.google.guava:failureaccess:1.0.1 (1 constraints: 140ae1b4) -com.google.guava:guava:27.0.1-jre (4 constraints: 613b78c4) -com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava (1 constraints: bd17c918) -com.google.j2objc:j2objc-annotations:1.1 (1 constraints: b609eba0) com.google.protobuf:protobuf-java:3.4.0 (1 constraints: f4102eb8) com.googlecode.java-diff-utils:diffutils:1.3.0 (1 constraints: df129c1b) junit:junit:4.13 (1 constraints: dc040031) org.assertj:assertj-core:3.17.0 (1 constraints: 3d054a3b) -org.checkerframework:checker-qual:2.5.3 (4 constraints: 202a1965) org.checkerframework:dataflow:2.5.3 (2 constraints: db23f14b) org.checkerframework:javacutil:2.5.3 (1 constraints: 470d3a1d) -org.codehaus.mojo:animal-sniffer-annotations:1.17 (1 constraints: ed09d8aa) org.hamcrest:hamcrest-core:1.3 (1 constraints: cc05fe3f) org.pcollections:pcollections:2.1.2 (1 constraints: f21022b8) From c52768d115d9d3aecb6515c302be8a794819cf08 Mon Sep 17 00:00:00 2001 From: Mattheo Ioannou Date: Thu, 25 Feb 2021 16:06:25 +0100 Subject: [PATCH 5/7] spotless --- .../src/test/java/com/palantir/logsafe/IterablesTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/iterables/src/test/java/com/palantir/logsafe/IterablesTest.java b/iterables/src/test/java/com/palantir/logsafe/IterablesTest.java index 200a6b62..afca3e3c 100644 --- a/iterables/src/test/java/com/palantir/logsafe/IterablesTest.java +++ b/iterables/src/test/java/com/palantir/logsafe/IterablesTest.java @@ -16,12 +16,12 @@ package com.palantir.logsafe; -import com.google.common.collect.ImmutableSet; -import org.junit.Test; - import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import com.google.common.collect.ImmutableSet; +import org.junit.Test; + public class IterablesTest { @Test public void getOnlyElementReturnsElement() { From 47c8112344b63b5c8a8ce565ce05568848930108 Mon Sep 17 00:00:00 2001 From: Mattheo Ioannou Date: Thu, 25 Feb 2021 16:15:51 +0100 Subject: [PATCH 6/7] update README --- README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README.md b/README.md index 313c37c7..32de1645 100644 --- a/README.md +++ b/README.md @@ -54,3 +54,28 @@ Annotate Preconditions error messages with named `SafeArg` and `UnsafeArg` as ap Preconditions.checkArgument(uname.size() > MAX_LEN, "username longer than max", UnsafeArg.of("uname", uname), SafeArg.of("max", MAX_LEN)); + +Iterables +============= +Guava Iterables equivalent which produces exceptions conforming to the SafeLoggable standard +(using the above Preconditions) + +Usage +----- + +Add dependency to gradle: + + compile "com.palantir.safe-logging:iterables" + +Example: + + // previously + import com.google.common.collect.Iterators; + ... + Preconditions.checkState(collection.size() == 1, "Expected exactly one element in collection", args); + return Iterators.getOnlyElement(collection.iterator()); + + // now + import com.palantir.logsafe.Iterables; + ... + Iterables.getOnlyElement(collection, args); \ No newline at end of file From c439c49d555621062737aa1dd84a9c9d21244184 Mon Sep 17 00:00:00 2001 From: Mattheo Ioannou Date: Tue, 2 Mar 2021 08:21:15 +0000 Subject: [PATCH 7/7] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 32de1645..0cb45f91 100644 --- a/README.md +++ b/README.md @@ -73,9 +73,9 @@ Example: import com.google.common.collect.Iterators; ... Preconditions.checkState(collection.size() == 1, "Expected exactly one element in collection", args); - return Iterators.getOnlyElement(collection.iterator()); + return Iterables.getOnlyElement(collection); // now import com.palantir.logsafe.Iterables; ... - Iterables.getOnlyElement(collection, args); \ No newline at end of file + Iterables.getOnlyElement(collection, args);