diff --git a/changelog/@unreleased/pr-1944.v2.yml b/changelog/@unreleased/pr-1944.v2.yml new file mode 100644 index 000000000..944f77093 --- /dev/null +++ b/changelog/@unreleased/pr-1944.v2.yml @@ -0,0 +1,4 @@ +type: feature +feature: Adds support for log safety annotations to external import types. For example, an alias of an external reference type will now have safety annotations on the class, getters, and setters. +links: + - https://github.com/palantir/conjure-java/pull/1944 diff --git a/conjure-java-core/src/integrationInput/java/com/palantir/product/ExternalLongTestServiceEndpoints.java b/conjure-java-core/src/integrationInput/java/com/palantir/product/ExternalLongTestServiceEndpoints.java new file mode 100644 index 000000000..9d5636493 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/com/palantir/product/ExternalLongTestServiceEndpoints.java @@ -0,0 +1,82 @@ +package com.palantir.product; + +import com.google.common.collect.ImmutableList; +import com.palantir.conjure.java.undertow.lib.Deserializer; +import com.palantir.conjure.java.undertow.lib.Endpoint; +import com.palantir.conjure.java.undertow.lib.TypeMarker; +import com.palantir.conjure.java.undertow.lib.UndertowRuntime; +import com.palantir.conjure.java.undertow.lib.UndertowService; +import com.palantir.tokens.auth.AuthHeader; +import io.undertow.server.HttpHandler; +import io.undertow.server.HttpServerExchange; +import io.undertow.util.HttpString; +import io.undertow.util.Methods; +import io.undertow.util.StatusCodes; +import java.io.IOException; +import java.util.List; +import javax.annotation.processing.Generated; + +@Generated("com.palantir.conjure.java.services.UndertowServiceHandlerGenerator") +public final class ExternalLongTestServiceEndpoints implements UndertowService { + private final UndertowExternalLongTestService delegate; + + private ExternalLongTestServiceEndpoints(UndertowExternalLongTestService delegate) { + this.delegate = delegate; + } + + public static UndertowService of(UndertowExternalLongTestService delegate) { + return new ExternalLongTestServiceEndpoints(delegate); + } + + @Override + public List endpoints(UndertowRuntime runtime) { + return ImmutableList.of(new TestExternalLongArgEndpoint(runtime, delegate)); + } + + private static final class TestExternalLongArgEndpoint implements HttpHandler, Endpoint { + private final UndertowRuntime runtime; + + private final UndertowExternalLongTestService delegate; + + private final Deserializer deserializer; + + TestExternalLongArgEndpoint(UndertowRuntime runtime, UndertowExternalLongTestService delegate) { + this.runtime = runtime; + this.delegate = delegate; + this.deserializer = runtime.bodySerDe().deserializer(new TypeMarker() {}, this); + } + + @Override + public void handleRequest(HttpServerExchange exchange) throws IOException { + AuthHeader authHeader = runtime.auth().header(exchange); + Long externalLong = deserializer.deserialize(exchange); + delegate.testExternalLongArg(authHeader, externalLong); + exchange.setStatusCode(StatusCodes.NO_CONTENT); + } + + @Override + public HttpString method() { + return Methods.POST; + } + + @Override + public String template() { + return "/external-long/test"; + } + + @Override + public String serviceName() { + return "ExternalLongTestService"; + } + + @Override + public String name() { + return "testExternalLongArg"; + } + + @Override + public HttpHandler handler() { + return this; + } + } +} diff --git a/conjure-java-core/src/integrationInput/java/com/palantir/product/ExternalLongUnionExample.java b/conjure-java-core/src/integrationInput/java/com/palantir/product/ExternalLongUnionExample.java new file mode 100644 index 000000000..dd58e391e --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/com/palantir/product/ExternalLongUnionExample.java @@ -0,0 +1,278 @@ +package com.palantir.product; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.annotation.JsonValue; +import com.palantir.logsafe.Preconditions; +import com.palantir.logsafe.Safe; +import com.palantir.logsafe.SafeArg; +import com.palantir.logsafe.exceptions.SafeIllegalArgumentException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.Function; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.processing.Generated; + +/** + * A union of a safe long. + */ +@Generated("com.palantir.conjure.java.types.UnionGenerator") +public final class ExternalLongUnionExample { + private final Base value; + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + private ExternalLongUnionExample(Base value) { + this.value = value; + } + + @JsonValue + private Base getValue() { + return value; + } + + public static ExternalLongUnionExample safeLong(@Safe long value) { + return new ExternalLongUnionExample(new SafeLongWrapper(value)); + } + + public static ExternalLongUnionExample unknown(@Safe String type, Object value) { + switch (Preconditions.checkNotNull(type, "Type is required")) { + case "safeLong": + throw new SafeIllegalArgumentException( + "Unknown type cannot be created as the provided type is known: safeLong"); + default: + return new ExternalLongUnionExample(new UnknownWrapper(type, Collections.singletonMap(type, value))); + } + } + + public T accept(Visitor visitor) { + return value.accept(visitor); + } + + @Override + public boolean equals(Object other) { + return this == other + || (other instanceof ExternalLongUnionExample && equalTo((ExternalLongUnionExample) other)); + } + + private boolean equalTo(ExternalLongUnionExample other) { + return this.value.equals(other.value); + } + + @Override + public int hashCode() { + return this.value.hashCode(); + } + + @Override + public String toString() { + return "ExternalLongUnionExample{value: " + value + '}'; + } + + public interface Visitor { + T visitSafeLong(@Safe long value); + + T visitUnknown(@Safe String unknownType, Object unknownValue); + + static SafeLongStageVisitorBuilder builder() { + return new VisitorBuilder(); + } + } + + private static final class VisitorBuilder + implements SafeLongStageVisitorBuilder, UnknownStageVisitorBuilder, Completed_StageVisitorBuilder { + private Function<@Safe Long, T> safeLongVisitor; + + private BiFunction<@Safe String, Object, T> unknownVisitor; + + @Override + public UnknownStageVisitorBuilder safeLong(@Nonnull Function<@Safe Long, T> safeLongVisitor) { + Preconditions.checkNotNull(safeLongVisitor, "safeLongVisitor cannot be null"); + this.safeLongVisitor = safeLongVisitor; + return this; + } + + @Override + public Completed_StageVisitorBuilder unknown(@Nonnull BiFunction<@Safe String, Object, T> unknownVisitor) { + Preconditions.checkNotNull(unknownVisitor, "unknownVisitor cannot be null"); + this.unknownVisitor = unknownVisitor; + return this; + } + + @Override + public Completed_StageVisitorBuilder unknown(@Nonnull Function<@Safe String, T> unknownVisitor) { + Preconditions.checkNotNull(unknownVisitor, "unknownVisitor cannot be null"); + this.unknownVisitor = (unknownType, _unknownValue) -> unknownVisitor.apply(unknownType); + return this; + } + + @Override + public Completed_StageVisitorBuilder throwOnUnknown() { + this.unknownVisitor = (unknownType, _unknownValue) -> { + throw new SafeIllegalArgumentException( + "Unknown variant of the 'ExternalLongUnionExample' union", + SafeArg.of("unknownType", unknownType)); + }; + return this; + } + + @Override + public Visitor build() { + final Function<@Safe Long, T> safeLongVisitor = this.safeLongVisitor; + final BiFunction<@Safe String, Object, T> unknownVisitor = this.unknownVisitor; + return new Visitor() { + @Override + public T visitSafeLong(long value) { + return safeLongVisitor.apply(value); + } + + @Override + public T visitUnknown(String unknownType, Object unknownValue) { + return unknownVisitor.apply(unknownType, unknownValue); + } + }; + } + } + + public interface SafeLongStageVisitorBuilder { + UnknownStageVisitorBuilder safeLong(@Nonnull Function<@Safe Long, T> safeLongVisitor); + } + + public interface UnknownStageVisitorBuilder { + Completed_StageVisitorBuilder unknown(@Nonnull BiFunction<@Safe String, Object, T> unknownVisitor); + + Completed_StageVisitorBuilder unknown(@Nonnull Function<@Safe String, T> unknownVisitor); + + Completed_StageVisitorBuilder throwOnUnknown(); + } + + public interface Completed_StageVisitorBuilder { + Visitor build(); + } + + @JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.EXISTING_PROPERTY, + property = "type", + visible = true, + defaultImpl = UnknownWrapper.class) + @JsonSubTypes(@JsonSubTypes.Type(SafeLongWrapper.class)) + @JsonIgnoreProperties(ignoreUnknown = true) + private interface Base { + T accept(Visitor visitor); + } + + @JsonTypeName("safeLong") + private static final class SafeLongWrapper implements Base { + private final long value; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + private SafeLongWrapper(@JsonSetter("safeLong") @Nonnull long value) { + Preconditions.checkNotNull(value, "safeLong cannot be null"); + this.value = value; + } + + @JsonProperty(value = "type", index = 0) + private String getType() { + return "safeLong"; + } + + @JsonProperty("safeLong") + private long getValue() { + return value; + } + + @Override + public T accept(Visitor visitor) { + return visitor.visitSafeLong(value); + } + + @Override + public boolean equals(@Nullable Object other) { + return this == other || (other instanceof SafeLongWrapper && equalTo((SafeLongWrapper) other)); + } + + private boolean equalTo(SafeLongWrapper other) { + return this.value == other.value; + } + + @Override + public int hashCode() { + return Long.hashCode(this.value); + } + + @Override + public String toString() { + return "SafeLongWrapper{value: " + value + '}'; + } + } + + private static final class UnknownWrapper implements Base { + private final String type; + + private final Map value; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + private UnknownWrapper(@JsonProperty("type") String type) { + this(type, new HashMap()); + } + + private UnknownWrapper(@Nonnull String type, @Nonnull Map value) { + Preconditions.checkNotNull(type, "type cannot be null"); + Preconditions.checkNotNull(value, "value cannot be null"); + this.type = type; + this.value = value; + } + + @JsonProperty + private String getType() { + return type; + } + + @JsonAnyGetter + private Map getValue() { + return value; + } + + @JsonAnySetter + private void put(String key, Object val) { + value.put(key, val); + } + + @Override + public T accept(Visitor visitor) { + return visitor.visitUnknown(type, value.get(type)); + } + + @Override + public boolean equals(@Nullable Object other) { + return this == other || (other instanceof UnknownWrapper && equalTo((UnknownWrapper) other)); + } + + private boolean equalTo(UnknownWrapper other) { + return this.type.equals(other.type) && this.value.equals(other.value); + } + + @Override + public int hashCode() { + int hash = 1; + hash = 31 * hash + this.type.hashCode(); + hash = 31 * hash + this.value.hashCode(); + return hash; + } + + @Override + public String toString() { + return "UnknownWrapper{type: " + type + ", value: " + value + '}'; + } + } +} diff --git a/conjure-java-core/src/integrationInput/java/com/palantir/product/SafeExternalLongAlias.java b/conjure-java-core/src/integrationInput/java/com/palantir/product/SafeExternalLongAlias.java new file mode 100644 index 000000000..c21a63351 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/com/palantir/product/SafeExternalLongAlias.java @@ -0,0 +1,50 @@ +package com.palantir.product; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.palantir.logsafe.Safe; +import javax.annotation.Nullable; +import javax.annotation.processing.Generated; + +@Safe +@Generated("com.palantir.conjure.java.types.AliasGenerator") +public final class SafeExternalLongAlias { + private final long value; + + private SafeExternalLongAlias(long value) { + this.value = value; + } + + @JsonValue + @Safe + public long get() { + return value; + } + + @Override + @Safe + public String toString() { + return String.valueOf(value); + } + + @Override + public boolean equals(@Nullable Object other) { + return this == other + || (other instanceof SafeExternalLongAlias && this.value == ((SafeExternalLongAlias) other).value); + } + + @Override + public int hashCode() { + return Long.hashCode(value); + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static SafeExternalLongAlias valueOf(@Safe String value) { + return of(Long.valueOf(value)); + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static SafeExternalLongAlias of(@Safe long value) { + return new SafeExternalLongAlias(value); + } +} diff --git a/conjure-java-core/src/integrationInput/java/com/palantir/product/SafeExternalLongExample.java b/conjure-java-core/src/integrationInput/java/com/palantir/product/SafeExternalLongExample.java new file mode 100644 index 000000000..dc77dfbf4 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/com/palantir/product/SafeExternalLongExample.java @@ -0,0 +1,268 @@ +package com.palantir.product; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.palantir.conjure.java.lib.internal.ConjureCollections; +import com.palantir.logsafe.Preconditions; +import com.palantir.logsafe.Safe; +import com.palantir.logsafe.SafeArg; +import com.palantir.logsafe.exceptions.SafeIllegalArgumentException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.processing.Generated; + +@Safe +@JsonDeserialize(builder = SafeExternalLongExample.Builder.class) +@Generated("com.palantir.conjure.java.types.BeanGenerator") +public final class SafeExternalLongExample { + private final long safeExternalLongValue; + + private final Optional optionalSafeExternalLong; + + private final List safeExternalLongList; + + private final Set safeExternalLongSet; + + private int memoizedHashCode; + + private SafeExternalLongExample( + long safeExternalLongValue, + Optional optionalSafeExternalLong, + List safeExternalLongList, + Set safeExternalLongSet) { + validateFields(optionalSafeExternalLong, safeExternalLongList, safeExternalLongSet); + this.safeExternalLongValue = safeExternalLongValue; + this.optionalSafeExternalLong = optionalSafeExternalLong; + this.safeExternalLongList = Collections.unmodifiableList(safeExternalLongList); + this.safeExternalLongSet = Collections.unmodifiableSet(safeExternalLongSet); + } + + @JsonProperty("safeExternalLongValue") + @Safe + public long getSafeExternalLongValue() { + return this.safeExternalLongValue; + } + + @JsonProperty("optionalSafeExternalLong") + @Safe + @JsonInclude(JsonInclude.Include.NON_ABSENT) + public Optional getOptionalSafeExternalLong() { + return this.optionalSafeExternalLong; + } + + @JsonProperty("safeExternalLongList") + @Safe + public List getSafeExternalLongList() { + return this.safeExternalLongList; + } + + @JsonProperty("safeExternalLongSet") + @Safe + public Set getSafeExternalLongSet() { + return this.safeExternalLongSet; + } + + @Override + public boolean equals(@Nullable Object other) { + return this == other || (other instanceof SafeExternalLongExample && equalTo((SafeExternalLongExample) other)); + } + + private boolean equalTo(SafeExternalLongExample other) { + if (this.memoizedHashCode != 0 + && other.memoizedHashCode != 0 + && this.memoizedHashCode != other.memoizedHashCode) { + return false; + } + return this.safeExternalLongValue == other.safeExternalLongValue + && this.optionalSafeExternalLong.equals(other.optionalSafeExternalLong) + && this.safeExternalLongList.equals(other.safeExternalLongList) + && this.safeExternalLongSet.equals(other.safeExternalLongSet); + } + + @Override + public int hashCode() { + int result = memoizedHashCode; + if (result == 0) { + int hash = 1; + hash = 31 * hash + Long.hashCode(this.safeExternalLongValue); + hash = 31 * hash + this.optionalSafeExternalLong.hashCode(); + hash = 31 * hash + this.safeExternalLongList.hashCode(); + hash = 31 * hash + this.safeExternalLongSet.hashCode(); + result = hash; + memoizedHashCode = result; + } + return result; + } + + @Override + @Safe + public String toString() { + return "SafeExternalLongExample{safeExternalLongValue: " + safeExternalLongValue + + ", optionalSafeExternalLong: " + optionalSafeExternalLong + ", safeExternalLongList: " + + safeExternalLongList + ", safeExternalLongSet: " + safeExternalLongSet + '}'; + } + + private static void validateFields( + Optional optionalSafeExternalLong, List safeExternalLongList, Set safeExternalLongSet) { + List missingFields = null; + missingFields = addFieldIfMissing(missingFields, optionalSafeExternalLong, "optionalSafeExternalLong"); + missingFields = addFieldIfMissing(missingFields, safeExternalLongList, "safeExternalLongList"); + missingFields = addFieldIfMissing(missingFields, safeExternalLongSet, "safeExternalLongSet"); + if (missingFields != null) { + throw new SafeIllegalArgumentException( + "Some required fields have not been set", SafeArg.of("missingFields", missingFields)); + } + } + + private static List addFieldIfMissing(List prev, Object fieldValue, String fieldName) { + List missingFields = prev; + if (fieldValue == null) { + if (missingFields == null) { + missingFields = new ArrayList<>(3); + } + missingFields.add(fieldName); + } + return missingFields; + } + + public static Builder builder() { + return new Builder(); + } + + @Generated("com.palantir.conjure.java.types.BeanBuilderGenerator") + public static final class Builder { + boolean _buildInvoked; + + private long safeExternalLongValue; + + private Optional<@Safe Long> optionalSafeExternalLong = Optional.empty(); + + private List<@Safe Long> safeExternalLongList = new ArrayList<>(); + + private Set<@Safe Long> safeExternalLongSet = new LinkedHashSet<>(); + + private boolean _safeExternalLongValueInitialized = false; + + private Builder() {} + + public Builder from(SafeExternalLongExample other) { + checkNotBuilt(); + safeExternalLongValue(other.getSafeExternalLongValue()); + optionalSafeExternalLong(other.getOptionalSafeExternalLong()); + safeExternalLongList(other.getSafeExternalLongList()); + safeExternalLongSet(other.getSafeExternalLongSet()); + return this; + } + + @JsonSetter("safeExternalLongValue") + public Builder safeExternalLongValue(@Safe long safeExternalLongValue) { + checkNotBuilt(); + this.safeExternalLongValue = safeExternalLongValue; + this._safeExternalLongValueInitialized = true; + return this; + } + + @JsonSetter(value = "optionalSafeExternalLong", nulls = Nulls.SKIP) + public Builder optionalSafeExternalLong(@Safe @Nonnull Optional optionalSafeExternalLong) { + checkNotBuilt(); + this.optionalSafeExternalLong = Preconditions.checkNotNull( + optionalSafeExternalLong, "optionalSafeExternalLong cannot be null") + .map(Function.identity()); + return this; + } + + public Builder optionalSafeExternalLong(@Safe long optionalSafeExternalLong) { + checkNotBuilt(); + this.optionalSafeExternalLong = Optional.of( + Preconditions.checkNotNull(optionalSafeExternalLong, "optionalSafeExternalLong cannot be null")); + return this; + } + + @JsonSetter(value = "safeExternalLongList", nulls = Nulls.SKIP, contentNulls = Nulls.FAIL) + public Builder safeExternalLongList(@Safe @Nonnull Iterable safeExternalLongList) { + checkNotBuilt(); + this.safeExternalLongList = ConjureCollections.newArrayList( + Preconditions.checkNotNull(safeExternalLongList, "safeExternalLongList cannot be null")); + return this; + } + + public Builder addAllSafeExternalLongList(@Safe @Nonnull Iterable safeExternalLongList) { + checkNotBuilt(); + ConjureCollections.addAll( + this.safeExternalLongList, + Preconditions.checkNotNull(safeExternalLongList, "safeExternalLongList cannot be null")); + return this; + } + + public Builder safeExternalLongList(@Safe long safeExternalLongList) { + checkNotBuilt(); + this.safeExternalLongList.add(safeExternalLongList); + return this; + } + + @JsonSetter(value = "safeExternalLongSet", nulls = Nulls.SKIP, contentNulls = Nulls.FAIL) + public Builder safeExternalLongSet(@Safe @Nonnull Iterable safeExternalLongSet) { + checkNotBuilt(); + this.safeExternalLongSet = ConjureCollections.newLinkedHashSet( + Preconditions.checkNotNull(safeExternalLongSet, "safeExternalLongSet cannot be null")); + return this; + } + + public Builder addAllSafeExternalLongSet(@Safe @Nonnull Iterable safeExternalLongSet) { + checkNotBuilt(); + ConjureCollections.addAll( + this.safeExternalLongSet, + Preconditions.checkNotNull(safeExternalLongSet, "safeExternalLongSet cannot be null")); + return this; + } + + public Builder safeExternalLongSet(@Safe long safeExternalLongSet) { + checkNotBuilt(); + this.safeExternalLongSet.add(safeExternalLongSet); + return this; + } + + private void validatePrimitiveFieldsHaveBeenInitialized() { + List missingFields = null; + missingFields = + addFieldIfMissing(missingFields, _safeExternalLongValueInitialized, "safeExternalLongValue"); + if (missingFields != null) { + throw new SafeIllegalArgumentException( + "Some required fields have not been set", SafeArg.of("missingFields", missingFields)); + } + } + + private static List addFieldIfMissing(List prev, boolean initialized, String fieldName) { + List missingFields = prev; + if (!initialized) { + if (missingFields == null) { + missingFields = new ArrayList<>(1); + } + missingFields.add(fieldName); + } + return missingFields; + } + + public SafeExternalLongExample build() { + checkNotBuilt(); + this._buildInvoked = true; + validatePrimitiveFieldsHaveBeenInitialized(); + return new SafeExternalLongExample( + safeExternalLongValue, optionalSafeExternalLong, safeExternalLongList, safeExternalLongSet); + } + + private void checkNotBuilt() { + Preconditions.checkState(!_buildInvoked, "Build has already been called"); + } + } +} diff --git a/conjure-java-core/src/integrationInput/java/com/palantir/product/UndertowExternalLongTestService.java b/conjure-java-core/src/integrationInput/java/com/palantir/product/UndertowExternalLongTestService.java new file mode 100644 index 000000000..8b289d352 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/com/palantir/product/UndertowExternalLongTestService.java @@ -0,0 +1,13 @@ +package com.palantir.product; + +import com.palantir.logsafe.DoNotLog; +import com.palantir.tokens.auth.AuthHeader; +import javax.annotation.processing.Generated; + +@Generated("com.palantir.conjure.java.services.UndertowServiceInterfaceGenerator") +public interface UndertowExternalLongTestService { + /** + * @apiNote {@code POST /external-long/test} + */ + void testExternalLongArg(AuthHeader authHeader, @DoNotLog long externalLong); +} diff --git a/conjure-java-core/src/integrationInput/java/test/prefix/com/palantir/product/ExternalLongUnionExample.java b/conjure-java-core/src/integrationInput/java/test/prefix/com/palantir/product/ExternalLongUnionExample.java new file mode 100644 index 000000000..498c0b6a9 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/test/prefix/com/palantir/product/ExternalLongUnionExample.java @@ -0,0 +1,268 @@ +package test.prefix.com.palantir.product; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.annotation.JsonValue; +import com.palantir.logsafe.Preconditions; +import com.palantir.logsafe.Safe; +import com.palantir.logsafe.SafeArg; +import com.palantir.logsafe.exceptions.SafeIllegalArgumentException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.processing.Generated; + +/** + * A union of a safe long. + */ +@Generated("com.palantir.conjure.java.types.UnionGenerator") +public final class ExternalLongUnionExample { + private final Base value; + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + private ExternalLongUnionExample(Base value) { + this.value = value; + } + + @JsonValue + private Base getValue() { + return value; + } + + public static ExternalLongUnionExample safeLong(@Safe long value) { + return new ExternalLongUnionExample(new SafeLongWrapper(value)); + } + + public static ExternalLongUnionExample unknown(@Safe String type, Object value) { + switch (Preconditions.checkNotNull(type, "Type is required")) { + case "safeLong": + throw new SafeIllegalArgumentException( + "Unknown type cannot be created as the provided type is known: safeLong"); + default: + return new ExternalLongUnionExample(new UnknownWrapper(type, Collections.singletonMap(type, value))); + } + } + + public T accept(Visitor visitor) { + return value.accept(visitor); + } + + @Override + public boolean equals(Object other) { + return this == other + || (other instanceof ExternalLongUnionExample && equalTo((ExternalLongUnionExample) other)); + } + + private boolean equalTo(ExternalLongUnionExample other) { + return this.value.equals(other.value); + } + + @Override + public int hashCode() { + return this.value.hashCode(); + } + + @Override + public String toString() { + return "ExternalLongUnionExample{value: " + value + '}'; + } + + public interface Visitor { + T visitSafeLong(@Safe long value); + + T visitUnknown(@Safe String unknownType); + + static SafeLongStageVisitorBuilder builder() { + return new VisitorBuilder(); + } + } + + private static final class VisitorBuilder + implements SafeLongStageVisitorBuilder, UnknownStageVisitorBuilder, Completed_StageVisitorBuilder { + private Function<@Safe Long, T> safeLongVisitor; + + private Function unknownVisitor; + + @Override + public UnknownStageVisitorBuilder safeLong(@Nonnull Function<@Safe Long, T> safeLongVisitor) { + Preconditions.checkNotNull(safeLongVisitor, "safeLongVisitor cannot be null"); + this.safeLongVisitor = safeLongVisitor; + return this; + } + + @Override + public Completed_StageVisitorBuilder unknown(@Nonnull Function unknownVisitor) { + Preconditions.checkNotNull(unknownVisitor, "unknownVisitor cannot be null"); + this.unknownVisitor = unknownVisitor; + return this; + } + + @Override + public Completed_StageVisitorBuilder throwOnUnknown() { + this.unknownVisitor = unknownType -> { + throw new SafeIllegalArgumentException( + "Unknown variant of the 'ExternalLongUnionExample' union", + SafeArg.of("unknownType", unknownType)); + }; + return this; + } + + @Override + public Visitor build() { + final Function<@Safe Long, T> safeLongVisitor = this.safeLongVisitor; + final Function unknownVisitor = this.unknownVisitor; + return new Visitor() { + @Override + public T visitSafeLong(long value) { + return safeLongVisitor.apply(value); + } + + @Override + public T visitUnknown(String value) { + return unknownVisitor.apply(value); + } + }; + } + } + + public interface SafeLongStageVisitorBuilder { + UnknownStageVisitorBuilder safeLong(@Nonnull Function<@Safe Long, T> safeLongVisitor); + } + + public interface UnknownStageVisitorBuilder { + Completed_StageVisitorBuilder unknown(@Nonnull Function unknownVisitor); + + Completed_StageVisitorBuilder throwOnUnknown(); + } + + public interface Completed_StageVisitorBuilder { + Visitor build(); + } + + @JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.EXISTING_PROPERTY, + property = "type", + visible = true, + defaultImpl = UnknownWrapper.class) + @JsonSubTypes(@JsonSubTypes.Type(SafeLongWrapper.class)) + @JsonIgnoreProperties(ignoreUnknown = true) + private interface Base { + T accept(Visitor visitor); + } + + @JsonTypeName("safeLong") + private static final class SafeLongWrapper implements Base { + private final long value; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + private SafeLongWrapper(@JsonSetter("safeLong") @Nonnull long value) { + Preconditions.checkNotNull(value, "safeLong cannot be null"); + this.value = value; + } + + @JsonProperty(value = "type", index = 0) + private String getType() { + return "safeLong"; + } + + @JsonProperty("safeLong") + private long getValue() { + return value; + } + + @Override + public T accept(Visitor visitor) { + return visitor.visitSafeLong(value); + } + + @Override + public boolean equals(@Nullable Object other) { + return this == other || (other instanceof SafeLongWrapper && equalTo((SafeLongWrapper) other)); + } + + private boolean equalTo(SafeLongWrapper other) { + return this.value == other.value; + } + + @Override + public int hashCode() { + return Long.hashCode(this.value); + } + + @Override + public String toString() { + return "SafeLongWrapper{value: " + value + '}'; + } + } + + private static final class UnknownWrapper implements Base { + private final String type; + + private final Map value; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + private UnknownWrapper(@JsonProperty("type") String type) { + this(type, new HashMap()); + } + + private UnknownWrapper(@Nonnull String type, @Nonnull Map value) { + Preconditions.checkNotNull(type, "type cannot be null"); + Preconditions.checkNotNull(value, "value cannot be null"); + this.type = type; + this.value = value; + } + + @JsonProperty + private String getType() { + return type; + } + + @JsonAnyGetter + private Map getValue() { + return value; + } + + @JsonAnySetter + private void put(String key, Object val) { + value.put(key, val); + } + + @Override + public T accept(Visitor visitor) { + return visitor.visitUnknown(type); + } + + @Override + public boolean equals(@Nullable Object other) { + return this == other || (other instanceof UnknownWrapper && equalTo((UnknownWrapper) other)); + } + + private boolean equalTo(UnknownWrapper other) { + return this.type.equals(other.type) && this.value.equals(other.value); + } + + @Override + public int hashCode() { + int hash = 1; + hash = 31 * hash + this.type.hashCode(); + hash = 31 * hash + this.value.hashCode(); + return hash; + } + + @Override + public String toString() { + return "UnknownWrapper{type: " + type + ", value: " + value + '}'; + } + } +} diff --git a/conjure-java-core/src/integrationInput/java/test/prefix/com/palantir/product/SafeExternalLongAlias.java b/conjure-java-core/src/integrationInput/java/test/prefix/com/palantir/product/SafeExternalLongAlias.java new file mode 100644 index 000000000..1fbf0d5db --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/test/prefix/com/palantir/product/SafeExternalLongAlias.java @@ -0,0 +1,50 @@ +package test.prefix.com.palantir.product; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.palantir.logsafe.Safe; +import javax.annotation.Nullable; +import javax.annotation.processing.Generated; + +@Safe +@Generated("com.palantir.conjure.java.types.AliasGenerator") +public final class SafeExternalLongAlias { + private final long value; + + private SafeExternalLongAlias(long value) { + this.value = value; + } + + @JsonValue + @Safe + public long get() { + return value; + } + + @Override + @Safe + public String toString() { + return String.valueOf(value); + } + + @Override + public boolean equals(@Nullable Object other) { + return this == other + || (other instanceof SafeExternalLongAlias && this.value == ((SafeExternalLongAlias) other).value); + } + + @Override + public int hashCode() { + return Long.hashCode(value); + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static SafeExternalLongAlias valueOf(@Safe String value) { + return of(Long.valueOf(value)); + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static SafeExternalLongAlias of(@Safe long value) { + return new SafeExternalLongAlias(value); + } +} diff --git a/conjure-java-core/src/integrationInput/java/test/prefix/com/palantir/product/SafeExternalLongExample.java b/conjure-java-core/src/integrationInput/java/test/prefix/com/palantir/product/SafeExternalLongExample.java new file mode 100644 index 000000000..6b1bbdefc --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/test/prefix/com/palantir/product/SafeExternalLongExample.java @@ -0,0 +1,270 @@ +package test.prefix.com.palantir.product; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.palantir.conjure.java.lib.internal.ConjureCollections; +import com.palantir.logsafe.Preconditions; +import com.palantir.logsafe.Safe; +import com.palantir.logsafe.SafeArg; +import com.palantir.logsafe.exceptions.SafeIllegalArgumentException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.processing.Generated; + +@Safe +@JsonDeserialize(builder = SafeExternalLongExample.Builder.class) +@Generated("com.palantir.conjure.java.types.BeanGenerator") +public final class SafeExternalLongExample { + private final long safeExternalLongValue; + + private final Optional optionalSafeExternalLong; + + private final List safeExternalLongList; + + private final Set safeExternalLongSet; + + private int memoizedHashCode; + + private SafeExternalLongExample( + long safeExternalLongValue, + Optional optionalSafeExternalLong, + List safeExternalLongList, + Set safeExternalLongSet) { + validateFields(optionalSafeExternalLong, safeExternalLongList, safeExternalLongSet); + this.safeExternalLongValue = safeExternalLongValue; + this.optionalSafeExternalLong = optionalSafeExternalLong; + this.safeExternalLongList = Collections.unmodifiableList(safeExternalLongList); + this.safeExternalLongSet = Collections.unmodifiableSet(safeExternalLongSet); + } + + @JsonProperty("safeExternalLongValue") + @Safe + public long getSafeExternalLongValue() { + return this.safeExternalLongValue; + } + + @JsonProperty("optionalSafeExternalLong") + @Safe + @JsonInclude(JsonInclude.Include.NON_ABSENT) + public Optional getOptionalSafeExternalLong() { + return this.optionalSafeExternalLong; + } + + @JsonProperty("safeExternalLongList") + @Safe + public List getSafeExternalLongList() { + return this.safeExternalLongList; + } + + @JsonProperty("safeExternalLongSet") + @Safe + public Set getSafeExternalLongSet() { + return this.safeExternalLongSet; + } + + @Override + public boolean equals(@Nullable Object other) { + return this == other || (other instanceof SafeExternalLongExample && equalTo((SafeExternalLongExample) other)); + } + + private boolean equalTo(SafeExternalLongExample other) { + if (this.memoizedHashCode != 0 + && other.memoizedHashCode != 0 + && this.memoizedHashCode != other.memoizedHashCode) { + return false; + } + return this.safeExternalLongValue == other.safeExternalLongValue + && this.optionalSafeExternalLong.equals(other.optionalSafeExternalLong) + && this.safeExternalLongList.equals(other.safeExternalLongList) + && this.safeExternalLongSet.equals(other.safeExternalLongSet); + } + + @Override + public int hashCode() { + int result = memoizedHashCode; + if (result == 0) { + int hash = 1; + hash = 31 * hash + Long.hashCode(this.safeExternalLongValue); + hash = 31 * hash + this.optionalSafeExternalLong.hashCode(); + hash = 31 * hash + this.safeExternalLongList.hashCode(); + hash = 31 * hash + this.safeExternalLongSet.hashCode(); + result = hash; + memoizedHashCode = result; + } + return result; + } + + @Override + @Safe + public String toString() { + return "SafeExternalLongExample{safeExternalLongValue: " + safeExternalLongValue + + ", optionalSafeExternalLong: " + optionalSafeExternalLong + ", safeExternalLongList: " + + safeExternalLongList + ", safeExternalLongSet: " + safeExternalLongSet + '}'; + } + + private static void validateFields( + Optional optionalSafeExternalLong, List safeExternalLongList, Set safeExternalLongSet) { + List missingFields = null; + missingFields = addFieldIfMissing(missingFields, optionalSafeExternalLong, "optionalSafeExternalLong"); + missingFields = addFieldIfMissing(missingFields, safeExternalLongList, "safeExternalLongList"); + missingFields = addFieldIfMissing(missingFields, safeExternalLongSet, "safeExternalLongSet"); + if (missingFields != null) { + throw new SafeIllegalArgumentException( + "Some required fields have not been set", SafeArg.of("missingFields", missingFields)); + } + } + + private static List addFieldIfMissing(List prev, Object fieldValue, String fieldName) { + List missingFields = prev; + if (fieldValue == null) { + if (missingFields == null) { + missingFields = new ArrayList<>(3); + } + missingFields.add(fieldName); + } + return missingFields; + } + + public static Builder builder() { + return new Builder(); + } + + @Generated("com.palantir.conjure.java.types.BeanBuilderGenerator") + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + boolean _buildInvoked; + + private long safeExternalLongValue; + + private Optional<@Safe Long> optionalSafeExternalLong = Optional.empty(); + + private List<@Safe Long> safeExternalLongList = new ArrayList<>(); + + private Set<@Safe Long> safeExternalLongSet = new LinkedHashSet<>(); + + private boolean _safeExternalLongValueInitialized = false; + + private Builder() {} + + public Builder from(SafeExternalLongExample other) { + checkNotBuilt(); + safeExternalLongValue(other.getSafeExternalLongValue()); + optionalSafeExternalLong(other.getOptionalSafeExternalLong()); + safeExternalLongList(other.getSafeExternalLongList()); + safeExternalLongSet(other.getSafeExternalLongSet()); + return this; + } + + @JsonSetter("safeExternalLongValue") + public Builder safeExternalLongValue(@Safe long safeExternalLongValue) { + checkNotBuilt(); + this.safeExternalLongValue = safeExternalLongValue; + this._safeExternalLongValueInitialized = true; + return this; + } + + @JsonSetter(value = "optionalSafeExternalLong", nulls = Nulls.SKIP) + public Builder optionalSafeExternalLong(@Safe @Nonnull Optional optionalSafeExternalLong) { + checkNotBuilt(); + this.optionalSafeExternalLong = Preconditions.checkNotNull( + optionalSafeExternalLong, "optionalSafeExternalLong cannot be null") + .map(Function.identity()); + return this; + } + + public Builder optionalSafeExternalLong(@Safe long optionalSafeExternalLong) { + checkNotBuilt(); + this.optionalSafeExternalLong = Optional.of( + Preconditions.checkNotNull(optionalSafeExternalLong, "optionalSafeExternalLong cannot be null")); + return this; + } + + @JsonSetter(value = "safeExternalLongList", nulls = Nulls.SKIP) + public Builder safeExternalLongList(@Safe @Nonnull Iterable safeExternalLongList) { + checkNotBuilt(); + this.safeExternalLongList = ConjureCollections.newArrayList( + Preconditions.checkNotNull(safeExternalLongList, "safeExternalLongList cannot be null")); + return this; + } + + public Builder addAllSafeExternalLongList(@Safe @Nonnull Iterable safeExternalLongList) { + checkNotBuilt(); + ConjureCollections.addAll( + this.safeExternalLongList, + Preconditions.checkNotNull(safeExternalLongList, "safeExternalLongList cannot be null")); + return this; + } + + public Builder safeExternalLongList(@Safe long safeExternalLongList) { + checkNotBuilt(); + this.safeExternalLongList.add(safeExternalLongList); + return this; + } + + @JsonSetter(value = "safeExternalLongSet", nulls = Nulls.SKIP) + public Builder safeExternalLongSet(@Safe @Nonnull Iterable safeExternalLongSet) { + checkNotBuilt(); + this.safeExternalLongSet = ConjureCollections.newLinkedHashSet( + Preconditions.checkNotNull(safeExternalLongSet, "safeExternalLongSet cannot be null")); + return this; + } + + public Builder addAllSafeExternalLongSet(@Safe @Nonnull Iterable safeExternalLongSet) { + checkNotBuilt(); + ConjureCollections.addAll( + this.safeExternalLongSet, + Preconditions.checkNotNull(safeExternalLongSet, "safeExternalLongSet cannot be null")); + return this; + } + + public Builder safeExternalLongSet(@Safe long safeExternalLongSet) { + checkNotBuilt(); + this.safeExternalLongSet.add(safeExternalLongSet); + return this; + } + + private void validatePrimitiveFieldsHaveBeenInitialized() { + List missingFields = null; + missingFields = + addFieldIfMissing(missingFields, _safeExternalLongValueInitialized, "safeExternalLongValue"); + if (missingFields != null) { + throw new SafeIllegalArgumentException( + "Some required fields have not been set", SafeArg.of("missingFields", missingFields)); + } + } + + private static List addFieldIfMissing(List prev, boolean initialized, String fieldName) { + List missingFields = prev; + if (!initialized) { + if (missingFields == null) { + missingFields = new ArrayList<>(1); + } + missingFields.add(fieldName); + } + return missingFields; + } + + public SafeExternalLongExample build() { + checkNotBuilt(); + this._buildInvoked = true; + validatePrimitiveFieldsHaveBeenInitialized(); + return new SafeExternalLongExample( + safeExternalLongValue, optionalSafeExternalLong, safeExternalLongList, safeExternalLongSet); + } + + private void checkNotBuilt() { + Preconditions.checkState(!_buildInvoked, "Build has already been called"); + } + } +} diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/ConjureTags.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/ConjureTags.java index f4b88a0b1..cf8bb4e82 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/ConjureTags.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/ConjureTags.java @@ -18,6 +18,7 @@ import com.google.common.collect.ImmutableSet; import com.palantir.conjure.java.types.SafetyEvaluator; +import com.palantir.conjure.java.util.SafetyUtils; import com.palantir.conjure.spec.ArgumentDefinition; import com.palantir.conjure.spec.EndpointDefinition; import com.palantir.conjure.spec.LogSafety; @@ -80,8 +81,10 @@ public static Optional validateArgument(ArgumentDefinition argument, public static Optional safety(ArgumentDefinition argument) { validateTags(argument); - if (argument.getSafety().isPresent()) { - return argument.getSafety(); + + Optional argumentSafety = SafetyUtils.getMaybeExternalSafety(argument); + if (argumentSafety.isPresent()) { + return argumentSafety; } Set tags = argument.getTags(); if (isSafe(tags)) { @@ -104,18 +107,19 @@ public static void validateTags(ArgumentDefinition argument) { isSafe(tags) ? "safe" : "unsafe", argument.getArgName())); } - if (argument.getSafety().isPresent()) { + Optional argumentSafety = SafetyUtils.getMaybeExternalSafety(argument); + if (argumentSafety.isPresent()) { if (markerSafety.isPresent()) { throw new IllegalStateException(String.format( "Unexpected 'safety: %s' value in addition to a '%s' marker on argument '%s'", - argument.getSafety().get().accept(DefFormatSafetyVisitor.INSTANCE), + argumentSafety.get().accept(DefFormatSafetyVisitor.INSTANCE), markerSafety.get().accept(MarkerNameLogSafetyVisitor.INSTANCE), argument.getArgName())); } if (isSafe(tags) || isUnsafe(tags)) { throw new IllegalStateException(String.format( "Unexpected 'safety: %s' value in addition to a '%s' tag on argument '%s'", - argument.getSafety().get().accept(DefFormatSafetyVisitor.INSTANCE), + argumentSafety.get().accept(DefFormatSafetyVisitor.INSTANCE), isSafe(tags) ? "safe" : "unsafe", argument.getArgName())); } diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/AliasGenerator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/AliasGenerator.java index 6c43b7211..83c4ce14d 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/AliasGenerator.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/AliasGenerator.java @@ -23,6 +23,7 @@ import com.palantir.conjure.java.lib.SafeLong; import com.palantir.conjure.java.util.Javadoc; import com.palantir.conjure.java.util.Packages; +import com.palantir.conjure.java.util.SafetyUtils; import com.palantir.conjure.java.visitor.MoreVisitors; import com.palantir.conjure.spec.AliasDefinition; import com.palantir.conjure.spec.ExternalReference; @@ -65,13 +66,12 @@ public static JavaFile generateAliasType( TypeMapper typeMapper, SafetyEvaluator safetyEvaluator, AliasDefinition typeDef, Options options) { com.palantir.conjure.spec.TypeName prefixedTypeName = Packages.getPrefixedName(typeDef.getTypeName(), options.packagePrefix()); - TypeName aliasTypeName = - ConjureAnnotations.withSafety(typeMapper.getClassName(typeDef.getAlias()), typeDef.getSafety()); + Optional safety = SafetyUtils.getMaybeExternalSafety(typeDef); + TypeName aliasTypeName = ConjureAnnotations.withSafety(typeMapper.getClassName(typeDef.getAlias()), safety); ClassName thisClass = ClassName.get(prefixedTypeName.getPackage(), prefixedTypeName.getName()); - Optional safety = typeDef.getSafety(); ImmutableList safetyAnnotations = ConjureAnnotations.safety(safety); - Optional computedSafety = safetyEvaluator.evaluate(typeDef.getAlias(), typeDef.getSafety()); + Optional computedSafety = safetyEvaluator.evaluate(typeDef.getAlias(), safety); ImmutableList computedSafetyAnnotations = ConjureAnnotations.safety(computedSafety); TypeSpec.Builder spec = TypeSpec.classBuilder(prefixedTypeName.getName()) diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/BeanBuilderAuxiliarySettersUtils.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/BeanBuilderAuxiliarySettersUtils.java index e6c61dbbc..d95bdcf40 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/BeanBuilderAuxiliarySettersUtils.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/BeanBuilderAuxiliarySettersUtils.java @@ -19,8 +19,10 @@ import com.palantir.conjure.java.ConjureAnnotations; import com.palantir.conjure.java.types.BeanGenerator.EnrichedField; import com.palantir.conjure.java.util.Javadoc; +import com.palantir.conjure.java.util.SafetyUtils; import com.palantir.conjure.java.visitor.DefaultTypeVisitor; import com.palantir.conjure.spec.FieldDefinition; +import com.palantir.conjure.spec.LogSafety; import com.palantir.conjure.spec.MapType; import com.palantir.conjure.spec.OptionalType; import com.palantir.conjure.spec.PrimitiveType; @@ -57,7 +59,7 @@ public static MethodSpec.Builder createCollectionSetterBuilder( .addParameter(Parameters.nonnullParameter( widenParameterIfPossible(field.type, type, typeMapper), field.name, - enriched.conjureDef().getSafety())); + SafetyUtils.getMaybeExternalSafety(enriched.conjureDef()))); } public static MethodSpec.Builder createOptionalSetterBuilder( @@ -68,13 +70,19 @@ public static MethodSpec.Builder createOptionalSetterBuilder( .addParameter(Parameters.nonnullParameter( typeMapper.getClassName(type.getItemType()), field.name, - enriched.conjureDef().getSafety())); + SafetyUtils.getMaybeExternalSafety(enriched.conjureDef()))); } public static MethodSpec.Builder createItemSetterBuilder( - EnrichedField enriched, Type itemType, TypeMapper typeMapper, ClassName returnClass) { + EnrichedField enriched, + Type itemType, + TypeMapper typeMapper, + ClassName returnClass, + Optional safety) { FieldSpec field = enriched.poetSpec(); - return publicSetter(enriched, returnClass).addParameter(typeMapper.getClassName(itemType), field.name); + return publicSetter(enriched, returnClass) + .addParameter( + typeMapper.getClassName(itemType).annotated(ConjureAnnotations.safety(safety)), field.name); } public static MethodSpec.Builder createMapSetterBuilder( diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/BeanBuilderGenerator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/BeanBuilderGenerator.java index d10fe9852..94ababa62 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/BeanBuilderGenerator.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/BeanBuilderGenerator.java @@ -27,6 +27,7 @@ import com.palantir.conjure.java.lib.internal.ConjureCollections; import com.palantir.conjure.java.types.BeanGenerator.EnrichedField; import com.palantir.conjure.java.util.JavaNameSanitizer; +import com.palantir.conjure.java.util.SafetyUtils; import com.palantir.conjure.java.util.TypeFunctions; import com.palantir.conjure.java.visitor.DefaultTypeVisitor; import com.palantir.conjure.java.visitor.DefaultableTypeVisitor; @@ -35,6 +36,7 @@ import com.palantir.conjure.spec.FieldDefinition; import com.palantir.conjure.spec.FieldName; import com.palantir.conjure.spec.ListType; +import com.palantir.conjure.spec.LogSafety; import com.palantir.conjure.spec.MapType; import com.palantir.conjure.spec.ObjectDefinition; import com.palantir.conjure.spec.OptionalType; @@ -263,7 +265,8 @@ private MethodSpec createFromObject(Collection enrichedFields, bo private EnrichedField createField(FieldName fieldName, FieldDefinition field) { Type type = field.getType(); - TypeName typeName = ConjureAnnotations.withSafety(typeMapper.getClassName(type), field.getSafety()); + TypeName typeName = + ConjureAnnotations.withSafety(typeMapper.getClassName(type), SafetyUtils.getMaybeExternalSafety(field)); FieldSpec.Builder spec = FieldSpec.builder(typeName, JavaNameSanitizer.sanitize(fieldName), Modifier.PRIVATE); if (type.accept(TypeVisitor.IS_LIST) || type.accept(TypeVisitor.IS_SET) || type.accept(TypeVisitor.IS_MAP)) { spec.initializer("new $T<>()", type.accept(COLLECTION_CONCRETE_TYPE)); @@ -328,7 +331,7 @@ private MethodSpec createSetter( .addParameter(Parameters.nonnullParameter( BeanBuilderAuxiliarySettersUtils.widenParameterIfPossible(field.type, type, typeMapper), field.name, - enriched.conjureDef().getSafety())) + SafetyUtils.getMaybeExternalSafety(enriched.conjureDef()))) .addCode(verifyNotBuilt()) .addCode(typeAwareAssignment(enriched, type, shouldClearFirst)); @@ -426,17 +429,18 @@ private boolean isByteBuffer(Type type) { private List createAuxiliarySetters(EnrichedField enriched, boolean override) { Type type = enriched.conjureDef().getType(); + Optional safety = SafetyUtils.getMaybeExternalSafety(enriched.conjureDef()); if (type.accept(TypeVisitor.IS_LIST)) { return ImmutableList.of( createCollectionSetter("addAll", enriched, override), - createItemSetter(enriched, type.accept(TypeVisitor.LIST).getItemType(), override)); + createItemSetter(enriched, type.accept(TypeVisitor.LIST).getItemType(), override, safety)); } if (type.accept(TypeVisitor.IS_SET)) { return ImmutableList.of( createCollectionSetter("addAll", enriched, override), - createItemSetter(enriched, type.accept(TypeVisitor.SET).getItemType(), override)); + createItemSetter(enriched, type.accept(TypeVisitor.SET).getItemType(), override, safety)); } if (type.accept(TypeVisitor.IS_MAP)) { @@ -488,9 +492,11 @@ private boolean isPrimitiveOptional(OptionalType optionalType) { optionalType.getItemType().accept(TypeVisitor.PRIMITIVE).get()); } - private MethodSpec createItemSetter(EnrichedField enriched, Type itemType, boolean override) { + private MethodSpec createItemSetter( + EnrichedField enriched, Type itemType, boolean override, Optional safety) { FieldSpec field = enriched.poetSpec(); - return BeanBuilderAuxiliarySettersUtils.createItemSetterBuilder(enriched, itemType, typeMapper, builderClass) + return BeanBuilderAuxiliarySettersUtils.createItemSetterBuilder( + enriched, itemType, typeMapper, builderClass, safety) .addAnnotations(ConjureAnnotations.override(override)) .addCode(verifyNotBuilt()) .addStatement("this.$1N.add($1N)", field.name) diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/BeanGenerator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/BeanGenerator.java index e10989824..4d7570fe2 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/BeanGenerator.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/BeanGenerator.java @@ -32,12 +32,14 @@ import com.palantir.conjure.java.util.JavaNameSanitizer; import com.palantir.conjure.java.util.Javadoc; import com.palantir.conjure.java.util.Packages; +import com.palantir.conjure.java.util.SafetyUtils; import com.palantir.conjure.java.util.TypeFunctions; import com.palantir.conjure.java.visitor.DefaultTypeVisitor; import com.palantir.conjure.java.visitor.MoreVisitors; import com.palantir.conjure.spec.FieldDefinition; import com.palantir.conjure.spec.FieldName; import com.palantir.conjure.spec.ListType; +import com.palantir.conjure.spec.LogSafety; import com.palantir.conjure.spec.MapType; import com.palantir.conjure.spec.ObjectDefinition; import com.palantir.conjure.spec.OptionalType; @@ -325,6 +327,7 @@ private static List generateMethodsForFinalStageField( List methodSpecs = new ArrayList<>(); Type type = enriched.conjureDef().getType(); FieldDefinition definition = enriched.conjureDef(); + Optional safety = SafetyUtils.getMaybeExternalSafety(definition); methodSpecs.add(MethodSpec.methodBuilder(JavaNameSanitizer.sanitize(enriched.fieldName())) .addParameter(ParameterSpec.builder( @@ -347,7 +350,7 @@ private static List generateMethodsForFinalStageField( .addModifiers(Modifier.ABSTRACT) .build()); methodSpecs.add(BeanBuilderAuxiliarySettersUtils.createItemSetterBuilder( - enriched, type.accept(TypeVisitor.LIST).getItemType(), typeMapper, returnClass) + enriched, type.accept(TypeVisitor.LIST).getItemType(), typeMapper, returnClass, safety) .addModifiers(Modifier.ABSTRACT) .build()); } @@ -358,7 +361,7 @@ private static List generateMethodsForFinalStageField( .addModifiers(Modifier.ABSTRACT) .build()); methodSpecs.add(BeanBuilderAuxiliarySettersUtils.createItemSetterBuilder( - enriched, type.accept(TypeVisitor.SET).getItemType(), typeMapper, returnClass) + enriched, type.accept(TypeVisitor.SET).getItemType(), typeMapper, returnClass, safety) .addModifiers(Modifier.ABSTRACT) .build()); } @@ -464,7 +467,7 @@ private static MethodSpec createGetter( .addAnnotation(AnnotationSpec.builder(JsonProperty.class) .addMember("value", "$S", field.fieldName().get()) .build()) - .addAnnotations(ConjureAnnotations.safety(field.conjureDef().getSafety())) + .addAnnotations(ConjureAnnotations.safety(SafetyUtils.getMaybeExternalSafety(field.conjureDef()))) .returns(field.poetSpec().type); Type conjureDefType = field.conjureDef().getType(); if (featureFlags.excludeEmptyOptionals()) { @@ -543,7 +546,7 @@ private static MethodSpec createStaticFactoryMethod(ImmutableList builder.addCode("return builder()"); fields.forEach(field -> builder.addParameter(ParameterSpec.builder( getTypeNameWithoutOptional(field.poetSpec()), field.poetSpec().name) - .addAnnotations(ConjureAnnotations.safety(field.conjureDef().getSafety())) + .addAnnotations(ConjureAnnotations.safety(SafetyUtils.getMaybeExternalSafety(field.conjureDef()))) .build())); // Follow order on adding methods on builder to comply with staged builders option if set sortedEnrichedFields(fields).map(EnrichedField::poetSpec).forEach(spec -> { diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/ErrorGenerator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/ErrorGenerator.java index af145d25b..840dc57d2 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/ErrorGenerator.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/ErrorGenerator.java @@ -27,6 +27,7 @@ import com.palantir.conjure.java.api.errors.ServiceException; import com.palantir.conjure.java.util.Javadoc; import com.palantir.conjure.java.util.Packages; +import com.palantir.conjure.java.util.SafetyUtils; import com.palantir.conjure.java.util.TypeFunctions; import com.palantir.conjure.spec.ConjureDefinition; import com.palantir.conjure.spec.ErrorDefinition; @@ -165,7 +166,7 @@ private JavaFile generateErrorTypesForNamespace( .build())) .map(arg -> { TypeName argumentTypeName = typeMapper.getClassName(arg.getType()); - Optional required = arg.getSafety(); + Optional required = SafetyUtils.getMaybeExternalSafety(arg); Optional typeSafety = safetyEvaluator.evaluate(arg.getType()); if (!SafetyEvaluator.allows(required, typeSafety)) { throw new IllegalStateException(String.format( @@ -182,7 +183,7 @@ private JavaFile generateErrorTypesForNamespace( return ParameterSpec.builder( argumentTypeName, arg.getFieldName().get()) - .addAnnotations(ConjureAnnotations.safety(arg.getSafety())) + .addAnnotations(ConjureAnnotations.safety(required)) .addJavadoc( "$L", StringUtils.appendIfMissing( diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/SafetyEvaluator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/SafetyEvaluator.java index 3e3dfa9fa..2f237032f 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/SafetyEvaluator.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/SafetyEvaluator.java @@ -16,6 +16,7 @@ package com.palantir.conjure.java.types; +import com.palantir.conjure.java.util.SafetyUtils; import com.palantir.conjure.java.util.TypeFunctions; import com.palantir.conjure.spec.AliasDefinition; import com.palantir.conjure.spec.ConjureDefinition; @@ -89,7 +90,8 @@ private TypeDefinitionSafetyVisitor(Map definitionMap, @Override public Optional visitAlias(AliasDefinition value) { - return with(value.getTypeName(), () -> getSafety(value.getAlias(), value.getSafety())); + return with( + value.getTypeName(), () -> getSafety(value.getAlias(), SafetyUtils.getMaybeExternalSafety(value))); } @Override @@ -102,7 +104,7 @@ public Optional visitObject(ObjectDefinition value) { return with(value.getTypeName(), () -> { Optional safety = Optional.of(LogSafety.SAFE); for (FieldDefinition field : value.getFields()) { - safety = combine(safety, getSafety(field.getType(), field.getSafety())); + safety = combine(safety, getSafety(field.getType(), SafetyUtils.getMaybeExternalSafety(field))); } return safety; }); @@ -113,7 +115,7 @@ public Optional visitUnion(UnionDefinition value) { return with(value.getTypeName(), () -> { Optional safety = UNKNOWN_UNION_VARINT_SAFETY; for (FieldDefinition variant : value.getUnion()) { - safety = combine(safety, getSafety(variant.getType(), variant.getSafety())); + safety = combine(safety, getSafety(variant.getType(), SafetyUtils.getMaybeExternalSafety(variant))); } return safety; }); @@ -187,9 +189,8 @@ public Optional visitReference(TypeName value) { } @Override - public Optional visitExternal(ExternalReference _value) { - // External types have unknown safety for now - return Optional.empty(); + public Optional visitExternal(ExternalReference value) { + return value.getSafety(); } @Override diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/UnionGenerator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/UnionGenerator.java index d504e8948..854a647de 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/UnionGenerator.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/UnionGenerator.java @@ -34,6 +34,7 @@ import com.palantir.conjure.java.util.JavaNameSanitizer; import com.palantir.conjure.java.util.Javadoc; import com.palantir.conjure.java.util.Packages; +import com.palantir.conjure.java.util.SafetyUtils; import com.palantir.conjure.java.util.StableCollectors; import com.palantir.conjure.java.util.TypeFunctions; import com.palantir.conjure.java.visitor.DefaultableTypeVisitor; @@ -104,7 +105,7 @@ public static JavaFile generateUnionType( .collect(StableCollectors.toLinkedMap( Function.identity(), entry -> ConjureAnnotations.withSafety( - typeMapper.getClassName(entry.getType()), entry.getSafety()))); + typeMapper.getClassName(entry.getType()), SafetyUtils.getMaybeExternalSafety(entry)))); List fields = ImmutableList.of(FieldSpec.builder(baseClass, VALUE_FIELD_NAME, Modifier.PRIVATE, Modifier.FINAL) .build()); @@ -179,7 +180,8 @@ private static List generateStaticFactories( MethodSpec.Builder builder = MethodSpec.methodBuilder(JavaNameSanitizer.sanitize(memberName)) .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addParameter(ParameterSpec.builder(memberType, variableName) - .addAnnotations(ConjureAnnotations.safety(memberTypeDef.getSafety())) + .addAnnotations(ConjureAnnotations.safety( + SafetyUtils.getMaybeExternalSafety(memberTypeDef))) .build()) .addStatement( "return new $T(new $T($L))", @@ -315,7 +317,7 @@ private static List generateMemberVisitMethods(Map sortedStageNameTypePairs(Map new NameTypeMetadata( sanitizeUnknown(entry.getKey().getFieldName().get()), entry.getValue(), - entry.getKey().getSafety())) + SafetyUtils.getMaybeExternalSafety(entry.getKey()))) .sorted(Comparator.comparing(p -> p.memberName)), Stream.of(NameTypeMetadata.UNKNOWN)); } diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/util/SafetyUtils.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/util/SafetyUtils.java new file mode 100644 index 000000000..0a094e5cb --- /dev/null +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/util/SafetyUtils.java @@ -0,0 +1,145 @@ +/* + * (c) Copyright 2023 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.conjure.java.util; + +import com.palantir.conjure.spec.AliasDefinition; +import com.palantir.conjure.spec.ArgumentDefinition; +import com.palantir.conjure.spec.ExternalReference; +import com.palantir.conjure.spec.FieldDefinition; +import com.palantir.conjure.spec.ListType; +import com.palantir.conjure.spec.LogSafety; +import com.palantir.conjure.spec.MapType; +import com.palantir.conjure.spec.OptionalType; +import com.palantir.conjure.spec.PrimitiveType; +import com.palantir.conjure.spec.SetType; +import com.palantir.conjure.spec.Type; +import com.palantir.conjure.spec.TypeName; +import java.util.Optional; + +public final class SafetyUtils { + + private SafetyUtils() {} + + public static Optional getMaybeExternalSafety(AliasDefinition alias) { + if (alias.getAlias().accept(WrapsExternalType.INSTANCE)) { + return alias.getAlias().accept(GetMaybeExternalSafety.INSTANCE); + } + return alias.getSafety(); + } + + public static Optional getMaybeExternalSafety(FieldDefinition field) { + if (field.getType().accept(WrapsExternalType.INSTANCE)) { + return field.getType().accept(GetMaybeExternalSafety.INSTANCE); + } + return field.getSafety(); + } + + public static Optional getMaybeExternalSafety(ArgumentDefinition argument) { + if (argument.getType().accept(WrapsExternalType.INSTANCE)) { + return argument.getType().accept(GetMaybeExternalSafety.INSTANCE); + } + return argument.getSafety(); + } + + private enum WrapsExternalType implements Type.Visitor { + INSTANCE; + + @Override + public java.lang.Boolean visitPrimitive(PrimitiveType _value) { + return false; + } + + @Override + public Boolean visitOptional(OptionalType value) { + return value.getItemType().accept(INSTANCE); + } + + @Override + public Boolean visitList(ListType value) { + return value.getItemType().accept(INSTANCE); + } + + @Override + public Boolean visitSet(SetType value) { + return value.getItemType().accept(INSTANCE); + } + + @Override + public Boolean visitMap(MapType _value) { + return false; + } + + @Override + public Boolean visitReference(TypeName _value) { + return false; + } + + @Override + public Boolean visitExternal(ExternalReference _value) { + return true; + } + + @Override + public Boolean visitUnknown(String _unknownType) { + return false; + } + } + + private enum GetMaybeExternalSafety implements Type.Visitor> { + INSTANCE; + + @Override + public Optional visitPrimitive(PrimitiveType _value) { + return Optional.empty(); + } + + @Override + public Optional visitOptional(OptionalType value) { + return value.getItemType().accept(INSTANCE); + } + + @Override + public Optional visitList(ListType value) { + return value.getItemType().accept(INSTANCE); + } + + @Override + public Optional visitSet(SetType value) { + return value.getItemType().accept(INSTANCE); + } + + @Override + public Optional visitMap(MapType _value) { + return Optional.empty(); + } + + @Override + public Optional visitReference(TypeName _value) { + return Optional.empty(); + } + + @Override + public Optional visitExternal(ExternalReference value) { + return value.getSafety(); + } + + @Override + public Optional visitUnknown(String _unknownType) { + return Optional.empty(); + } + } +} diff --git a/conjure-java-core/src/test/java/com/palantir/conjure/java/ConjureTagsTest.java b/conjure-java-core/src/test/java/com/palantir/conjure/java/ConjureTagsTest.java index 1fd6b0aff..1bf0fdb0c 100644 --- a/conjure-java-core/src/test/java/com/palantir/conjure/java/ConjureTagsTest.java +++ b/conjure-java-core/src/test/java/com/palantir/conjure/java/ConjureTagsTest.java @@ -192,6 +192,37 @@ void testTagAndTypeSafetyDisagreement() { .hasMessageContaining("Declared argument safety is incompatible with the provided type"); } + @Test + void testExternalImport_AtImportTime() { + SafetyEvaluator safetyEvaluator = new SafetyEvaluator(ImmutableMap.of()); + Type external = Type.external(ExternalReference.builder() + .externalReference(TypeName.of("Long", "java.lang")) + .fallback(Type.primitive(PrimitiveType.STRING)) + .safety(LogSafety.DO_NOT_LOG) + .build()); + ArgumentDefinition argument = ArgumentDefinition.builder() + .argName(ArgumentName.of("testArgument")) + .type(external) + .paramType(ParameterType.body(BodyParameterType.of())) + .build(); + assertThat(ConjureTags.validateArgument(argument, safetyEvaluator)).hasValue(LogSafety.DO_NOT_LOG); + } + + @Test + void testExternalImport_NoSafety() { + SafetyEvaluator safetyEvaluator = new SafetyEvaluator(ImmutableMap.of()); + Type external = Type.external(ExternalReference.builder() + .externalReference(TypeName.of("Long", "java.lang")) + .fallback(Type.primitive(PrimitiveType.STRING)) + .build()); + ArgumentDefinition argument = ArgumentDefinition.builder() + .argName(ArgumentName.of("testArgument")) + .type(external) + .paramType(ParameterType.body(BodyParameterType.of())) + .build(); + assertThat(ConjureTags.validateArgument(argument, safetyEvaluator)).isEmpty(); + } + private static ArgumentDefinition tags(String... tags) { return ArgumentDefinition.builder() .argName(ArgumentName.of("name")) diff --git a/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java b/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java index cd2bc5588..2326f42c2 100644 --- a/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java +++ b/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java @@ -569,7 +569,8 @@ public static void beforeClass() throws IOException { ConjureDefinition def = Conjure.parse(ImmutableList.of( new File("src/test/resources/ete-service.yml"), new File("src/test/resources/ete-binary.yml"), - new File("src/test/resources/alias-test-service.yml"))); + new File("src/test/resources/alias-test-service.yml"), + new File("src/test/resources/external-long-test-service.yml"))); Options options = Options.builder() .undertowServicePrefix(true) .nonNullCollections(true) diff --git a/conjure-java-core/src/test/java/com/palantir/conjure/java/types/ExternalImportSafetyTests.java b/conjure-java-core/src/test/java/com/palantir/conjure/java/types/ExternalImportSafetyTests.java new file mode 100644 index 000000000..3dc11e81c --- /dev/null +++ b/conjure-java-core/src/test/java/com/palantir/conjure/java/types/ExternalImportSafetyTests.java @@ -0,0 +1,229 @@ +/* + * (c) Copyright 2023 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.conjure.java.types; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.palantir.logsafe.DoNotLog; +import com.palantir.logsafe.Safe; +import com.palantir.product.ExternalLongUnionExample; +import com.palantir.product.SafeExternalLongAlias; +import com.palantir.product.SafeExternalLongExample; +import com.palantir.product.UndertowExternalLongTestService; +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedParameterizedType; +import java.lang.reflect.AnnotatedType; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; + +public class ExternalImportSafetyTests { + + @Test + public void testAliasAnnotations() { + assertThat(SafeExternalLongAlias.class).hasAnnotation(Safe.class); + + assertMethodHasAnnotation(SafeExternalLongAlias.class, "toString", Safe.class); + assertMethodHasAnnotation(SafeExternalLongAlias.class, "get", Safe.class); + assertMethodParamHasAnnotation(SafeExternalLongAlias.class, "valueOf", "value", Safe.class); + assertMethodParamHasAnnotation(SafeExternalLongAlias.class, "of", "value", Safe.class); + } + + @Test + public void testObjectAnnotations() { + assertThat(SafeExternalLongExample.class).hasAnnotation(Safe.class); + + assertMethodHasAnnotation(SafeExternalLongExample.class, "toString", Safe.class); + assertMethodHasAnnotation(SafeExternalLongExample.class, "getSafeExternalLongValue", Safe.class); + + Class builder = getMatchingSubclass(SafeExternalLongExample.class, "Builder"); + assertMethodParamHasAnnotation(builder, "safeExternalLongValue", "safeExternalLongValue", Safe.class); + } + + @Test + public void testServiceAnnotations() { + assertMethodParamHasAnnotation( + UndertowExternalLongTestService.class, "testExternalLongArg", "externalLong", DoNotLog.class); + } + + @Test + public void testUnionAnnotations() { + assertMethodParamHasAnnotation(ExternalLongUnionExample.class, "safeLong", "value", Safe.class); + assertMethodParamHasAnnotation(ExternalLongUnionExample.class, "unknown", "type", Safe.class); + + Class visitor = getExactlyMatchingSubclass( + ExternalLongUnionExample.class, "com.palantir.product.ExternalLongUnionExample$Visitor"); + assertMethodParamHasAnnotation(visitor, "visitSafeLong", "value", Safe.class); + assertMethodParamHasAnnotation(visitor, "visitUnknown", "unknownType", Safe.class); + + Class visitorBuilder = getExactlyMatchingSubclass( + ExternalLongUnionExample.class, "com.palantir.product.ExternalLongUnionExample$VisitorBuilder"); + assertFieldTypeParamHasAnnotation(visitorBuilder, "safeLongVisitor", "Long", Safe.class); + assertFieldTypeParamHasAnnotation(visitorBuilder, "unknownVisitor", "String", Safe.class); + assertMethodParamWithTypeParameterHasAnnotation(visitorBuilder, "safeLong", "safeLong", "Long", Safe.class); + assertMethodParamWithTypeParameterHasAnnotation(visitorBuilder, "unknown", "unknown", "String", Safe.class); + + Class stageVisitorBuilder = + getMatchingSubclass(ExternalLongUnionExample.class, "SafeLongStageVisitorBuilder"); + assertMethodParamWithTypeParameterHasAnnotation( + stageVisitorBuilder, "safeLong", "safeLongVisitor", "Long", Safe.class); + + Class unknownStageVisitorBuilder = + getMatchingSubclass(ExternalLongUnionExample.class, "UnknownStageVisitorBuilder"); + assertMethodParamWithTypeParameterHasAnnotation( + unknownStageVisitorBuilder, "unknown", "unknownVisitor", "String", Safe.class); + } + + @Test + public void testOptionalAnnotations() { + assertMethodHasAnnotation(SafeExternalLongExample.class, "getOptionalSafeExternalLong", Safe.class); + + Class builderSubclass = getMatchingSubclass(SafeExternalLongExample.class, "$Builder"); + assertFieldTypeParamHasAnnotation(builderSubclass, "optionalSafeExternalLong", "Long", Safe.class); + assertMethodParamHasAnnotation( + builderSubclass, "optionalSafeExternalLong", "optionalSafeExternalLong", Safe.class); + } + + @Test + public void testListAnnotations() { + assertMethodHasAnnotation(SafeExternalLongExample.class, "getSafeExternalLongList", Safe.class); + + Class builderSubclass = getMatchingSubclass(SafeExternalLongExample.class, "$Builder"); + assertFieldTypeParamHasAnnotation(builderSubclass, "safeExternalLongList", "Long", Safe.class); + assertMethodParamHasAnnotation(builderSubclass, "safeExternalLongList", "safeExternalLongList", Safe.class); + assertMethodParamHasAnnotation( + builderSubclass, "addAllSafeExternalLongList", "safeExternalLongList", Safe.class); + } + + @Test + public void testSetAnnotations() { + assertMethodHasAnnotation(SafeExternalLongExample.class, "getSafeExternalLongSet", Safe.class); + + Class builderSubclass = getMatchingSubclass(SafeExternalLongExample.class, "$Builder"); + assertFieldTypeParamHasAnnotation(builderSubclass, "safeExternalLongSet", "Long", Safe.class); + assertMethodParamHasAnnotation(builderSubclass, "safeExternalLongSet", "safeExternalLongSet", Safe.class); + assertMethodParamHasAnnotation(builderSubclass, "addAllSafeExternalLongSet", "safeExternalLongSet", Safe.class); + } + + private void assertMethodHasAnnotation( + Class parentClass, String methodName, Class annotation) { + Stream desiredMethods = getMatchingMethods(parentClass, methodName); + assertThat(desiredMethods) + .withFailMessage(String.format( + "Expected %s:%s to have annotation %s", + parentClass.getName(), methodName, annotation.getName())) + .allMatch(method -> method.isAnnotationPresent(annotation)); + } + + private void assertMethodParamHasAnnotation( + Class parentClass, String methodName, String parameterName, Class annotation) { + Stream desiredMethods = getMatchingMethods(parentClass, methodName); + assertThat(desiredMethods) + .withFailMessage(String.format( + "Expected %s:%s parameter %s to have annotation %s", + parentClass.getName(), methodName, parameterName, annotation.getName())) + .map(method -> getMatchingParameter(method, parameterName)) + .allMatch(parameter -> parameter.isAnnotationPresent(annotation)); + } + + private void assertMethodParamWithTypeParameterHasAnnotation( + Class parentClass, + String methodName, + String parameterName, + String typeParameter, + Class annotation) { + Stream desiredMethods = getMatchingMethods(parentClass, methodName); + Stream annotatedTypes = desiredMethods.map( + method -> getMatchingParameter(method, parameterName).getAnnotatedType()); + assertThat(annotatedTypes) + .withFailMessage(String.format( + "Expected %s:%s parameter %s of type %s to have annotation %s", + parentClass.getName(), methodName, parameterName, typeParameter, annotation.getName())) + .map(annotatedType -> getAnnotatedTypeParameter(annotatedType, typeParameter)) + .allMatch(t -> t.isAnnotationPresent(annotation)); + } + + private void assertFieldTypeParamHasAnnotation( + Class parentClass, String fieldName, String typeName, Class annotation) { + Stream desiredFields = Arrays.stream(parentClass.getDeclaredFields()) + .filter(field -> field.getName().contains(fieldName)); + Stream annotatedTypeParameters = + desiredFields.map(Field::getAnnotatedType).map(type -> getAnnotatedTypeParameter(type, typeName)); + assertThat(annotatedTypeParameters) + .withFailMessage(String.format( + "Expected %s:%s of type %s to have annotation %s", + parentClass.getName(), fieldName, typeName, annotation.getName())) + .allMatch(t -> t.isAnnotationPresent(annotation)); + } + + private Stream getMatchingMethods(Class parentClass, String methodName) { + List methods = Arrays.stream(parentClass.getMethods()) + .filter(method -> method.getName().equals(methodName)) + .collect(Collectors.toList()); + assertThat(methods) + .withFailMessage(String.format("Expected method %s on class %s", methodName, parentClass.getName())) + .isNotEmpty(); + return methods.stream(); + } + + private Class getMatchingSubclass(Class parentClass, String subclassName) { + Optional> subclassIfExists = Arrays.stream(parentClass.getDeclaredClasses()) + .filter(subclass -> subclass.getName().contains(subclassName)) + .findAny(); + assertThat(subclassIfExists) + .withFailMessage(String.format("Expected %s:%s to exist", parentClass, subclassName)) + .isPresent(); + return subclassIfExists.get(); + } + + private Class getExactlyMatchingSubclass(Class parentClass, String subclassName) { + Optional> subclassIfExists = Arrays.stream(parentClass.getDeclaredClasses()) + .filter(subclass -> subclass.getName().equals(subclassName)) + .findAny(); + assertThat(subclassIfExists) + .withFailMessage(String.format("Expected %s:%s to exist", parentClass, subclassName)) + .isPresent(); + return subclassIfExists.get(); + } + + private Parameter getMatchingParameter(Method method, String parameterName) { + Optional parameterIfExists = Arrays.stream(method.getParameters()) + .filter(parameter -> parameter.getName().contains(parameterName)) + .findAny(); + assertThat(parameterIfExists) + .withFailMessage(String.format("Expected to find parameter %s on method %s", parameterName, method)) + .isPresent(); + return parameterIfExists.get(); + } + + private AnnotatedType getAnnotatedTypeParameter(AnnotatedType parameterizedType, String parameter) { + Optional typeParameterIfExists = Arrays.stream( + ((AnnotatedParameterizedType) parameterizedType).getAnnotatedActualTypeArguments()) + .filter(t -> t.getType().getTypeName().contains(parameter)) + .findAny(); + assertThat(typeParameterIfExists) + .withFailMessage("Expected type parameter %s on type %s", parameter, parameterizedType) + .isPresent(); + return typeParameterIfExists.get(); + } +} diff --git a/conjure-java-core/src/test/java/com/palantir/conjure/java/types/SafetyEvaluatorTest.java b/conjure-java-core/src/test/java/com/palantir/conjure/java/types/SafetyEvaluatorTest.java index 9f77efeef..922ec9459 100644 --- a/conjure-java-core/src/test/java/com/palantir/conjure/java/types/SafetyEvaluatorTest.java +++ b/conjure-java-core/src/test/java/com/palantir/conjure/java/types/SafetyEvaluatorTest.java @@ -19,11 +19,13 @@ import static org.assertj.core.api.Assertions.assertThat; import com.google.common.collect.Iterables; +import com.palantir.conjure.defs.SafetyDeclarationRequirements; import com.palantir.conjure.defs.validator.ConjureDefinitionValidator; import com.palantir.conjure.spec.AliasDefinition; import com.palantir.conjure.spec.ConjureDefinition; import com.palantir.conjure.spec.Documentation; import com.palantir.conjure.spec.EnumDefinition; +import com.palantir.conjure.spec.ExternalReference; import com.palantir.conjure.spec.FieldDefinition; import com.palantir.conjure.spec.FieldName; import com.palantir.conjure.spec.LogSafety; @@ -35,7 +37,12 @@ import com.palantir.conjure.spec.TypeName; import com.palantir.conjure.spec.UnionDefinition; import java.util.Optional; +import java.util.stream.Stream; +import org.junit.jupiter.api.Named; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; class SafetyEvaluatorTest { private static final String PACKAGE = "package"; @@ -130,11 +137,44 @@ void testMapSafeKey() { .types(object) .types(SAFE_ALIAS) .build(); - ConjureDefinitionValidator.validateAll(conjureDef); + ConjureDefinitionValidator.validateAll(conjureDef, SafetyDeclarationRequirements.ALLOWED); SafetyEvaluator evaluator = new SafetyEvaluator(conjureDef); assertThat(evaluator.evaluate(object)).isEmpty(); } + private static Stream providesExternalRefTypes_ImportTime() { + Type external = Type.external(ExternalReference.builder() + .externalReference(TypeName.of("Long", "java.lang")) + .fallback(Type.primitive(PrimitiveType.STRING)) + .safety(LogSafety.DO_NOT_LOG) + .build()); + return getTypes(external); + } + + @ParameterizedTest + @MethodSource("providesExternalRefTypes_ImportTime") + void testExternalRefType_AtImportTime(TypeDefinition typeDefinition, ConjureDefinition conjureDef) { + ConjureDefinitionValidator.validateAll(conjureDef, SafetyDeclarationRequirements.ALLOWED); + SafetyEvaluator evaluator = new SafetyEvaluator(conjureDef); + assertThat(evaluator.evaluate(typeDefinition)).hasValue(LogSafety.DO_NOT_LOG); + } + + private static Stream providesExternalRefTypes_NoSafety() { + Type external = Type.external(ExternalReference.builder() + .externalReference(TypeName.of("Long", "java.lang")) + .fallback(Type.primitive(PrimitiveType.STRING)) + .build()); + return getTypes(external); + } + + @ParameterizedTest + @MethodSource("providesExternalRefTypes_NoSafety") + void testExternalRefType_NoSafety(TypeDefinition typeDefinition, ConjureDefinition conjureDef) { + ConjureDefinitionValidator.validateAll(conjureDef, SafetyDeclarationRequirements.ALLOWED); + SafetyEvaluator evaluator = new SafetyEvaluator(conjureDef); + assertThat(evaluator.evaluate(typeDefinition)).isEmpty(); + } + @Test void testMapSafetyUnsafeValue() { TypeDefinition object = TypeDefinition.object(ObjectDefinition.builder() @@ -150,7 +190,7 @@ void testMapSafetyUnsafeValue() { .types(SAFE_ALIAS) .types(UNSAFE_ALIAS) .build(); - ConjureDefinitionValidator.validateAll(conjureDef); + ConjureDefinitionValidator.validateAll(conjureDef, SafetyDeclarationRequirements.ALLOWED); SafetyEvaluator evaluator = new SafetyEvaluator(conjureDef); assertThat(evaluator.evaluate(object)).hasValue(LogSafety.UNSAFE); } @@ -170,7 +210,7 @@ void testMapSafetySafeKeyAndValue() { .types(SAFE_ALIAS) .types(UNSAFE_ALIAS) .build(); - ConjureDefinitionValidator.validateAll(conjureDef); + ConjureDefinitionValidator.validateAll(conjureDef, SafetyDeclarationRequirements.ALLOWED); SafetyEvaluator evaluator = new SafetyEvaluator(conjureDef); assertThat(evaluator.evaluate(object)).hasValue(LogSafety.SAFE); } @@ -203,7 +243,7 @@ void testNested() { .types(secondObject) .types(UNSAFE_ALIAS) .build(); - ConjureDefinitionValidator.validateAll(conjureDef); + ConjureDefinitionValidator.validateAll(conjureDef, SafetyDeclarationRequirements.ALLOWED); SafetyEvaluator evaluator = new SafetyEvaluator(conjureDef); assertThat(evaluator.evaluate(firstObject)).hasValue(LogSafety.UNSAFE); } @@ -222,7 +262,7 @@ void testPrimitiveSafety() { .build()) .build())) .build(); - ConjureDefinitionValidator.validateAll(conjureDef); + ConjureDefinitionValidator.validateAll(conjureDef, SafetyDeclarationRequirements.ALLOWED); SafetyEvaluator evaluator = new SafetyEvaluator(conjureDef); assertThat(evaluator.evaluate(Iterables.getOnlyElement(conjureDef.getTypes()))) .hasValue(LogSafety.UNSAFE); @@ -241,7 +281,7 @@ void testNoSafety() { .build()) .build())) .build(); - ConjureDefinitionValidator.validateAll(conjureDef); + ConjureDefinitionValidator.validateAll(conjureDef, SafetyDeclarationRequirements.ALLOWED); SafetyEvaluator evaluator = new SafetyEvaluator(conjureDef); assertThat(evaluator.evaluate(Iterables.getOnlyElement(conjureDef.getTypes()))) .isEmpty(); @@ -254,7 +294,7 @@ void testEmptyUnion() { .types(TypeDefinition.union( UnionDefinition.builder().typeName(FOO).build())) .build(); - ConjureDefinitionValidator.validateAll(conjureDef); + ConjureDefinitionValidator.validateAll(conjureDef, SafetyDeclarationRequirements.ALLOWED); SafetyEvaluator evaluator = new SafetyEvaluator(conjureDef); assertThat(evaluator.evaluate(Iterables.getOnlyElement(conjureDef.getTypes()))) .as("No guarantees can be made about future union values, " @@ -275,7 +315,7 @@ void testUnsafeElementUnion() { .build()) .build())) .build(); - ConjureDefinitionValidator.validateAll(conjureDef); + ConjureDefinitionValidator.validateAll(conjureDef, SafetyDeclarationRequirements.ALLOWED); SafetyEvaluator evaluator = new SafetyEvaluator(conjureDef); assertThat(evaluator.evaluate(Iterables.getOnlyElement(conjureDef.getTypes()))) .hasValue(LogSafety.UNSAFE); @@ -294,7 +334,7 @@ void testDoNotLogElementUnion() { .build()) .build())) .build(); - ConjureDefinitionValidator.validateAll(conjureDef); + ConjureDefinitionValidator.validateAll(conjureDef, SafetyDeclarationRequirements.ALLOWED); SafetyEvaluator evaluator = new SafetyEvaluator(conjureDef); assertThat(evaluator.evaluate(Iterables.getOnlyElement(conjureDef.getTypes()))) .hasValue(LogSafety.DO_NOT_LOG); @@ -307,7 +347,7 @@ void testEmptyObject() { .types(TypeDefinition.object( ObjectDefinition.builder().typeName(FOO).build())) .build(); - ConjureDefinitionValidator.validateAll(conjureDef); + ConjureDefinitionValidator.validateAll(conjureDef, SafetyDeclarationRequirements.ALLOWED); SafetyEvaluator evaluator = new SafetyEvaluator(conjureDef); assertThat(evaluator.evaluate(Iterables.getOnlyElement(conjureDef.getTypes()))) .hasValue(LogSafety.SAFE); @@ -320,9 +360,52 @@ void testEmptyEnum() { .types(TypeDefinition.enum_( EnumDefinition.builder().typeName(FOO).build())) .build(); - ConjureDefinitionValidator.validateAll(conjureDef); + ConjureDefinitionValidator.validateAll(conjureDef, SafetyDeclarationRequirements.ALLOWED); SafetyEvaluator evaluator = new SafetyEvaluator(conjureDef); assertThat(evaluator.evaluate(Iterables.getOnlyElement(conjureDef.getTypes()))) .hasValue(LogSafety.SAFE); } + + private static Stream getTypes(Type externalReference) { + TypeDefinition objectType = TypeDefinition.object(ObjectDefinition.builder() + .typeName(FOO) + .fields(FieldDefinition.builder() + .fieldName(FieldName.of("import")) + .type(externalReference) + .docs(DOCS) + .build()) + .build()); + ConjureDefinition conjureObjectDef = ConjureDefinition.builder() + .version(1) + .types(objectType) + .types(UNSAFE_ALIAS) + .build(); + + TypeDefinition aliasType = TypeDefinition.alias( + AliasDefinition.builder().typeName(FOO).alias(externalReference).build()); + ConjureDefinition conjureAliasDef = ConjureDefinition.builder() + .version(1) + .types(aliasType) + .types(UNSAFE_ALIAS) + .build(); + + TypeDefinition unionType = TypeDefinition.union(UnionDefinition.builder() + .union(FieldDefinition.builder() + .fieldName(FieldName.of("importOne")) + .type(externalReference) + .docs(DOCS) + .build()) + .typeName(FOO) + .build()); + ConjureDefinition conjureUnionDef = ConjureDefinition.builder() + .version(1) + .types(unionType) + .types(UNSAFE_ALIAS) + .build(); + + return Stream.of( + Arguments.of(Named.of("Object", objectType), conjureObjectDef), + Arguments.of(Named.of("Alias", aliasType), conjureAliasDef), + Arguments.of(Named.of("Union", unionType), conjureUnionDef)); + } } diff --git a/conjure-java-core/src/test/java/com/palantir/conjure/java/util/SafetyUtilsTests.java b/conjure-java-core/src/test/java/com/palantir/conjure/java/util/SafetyUtilsTests.java new file mode 100644 index 000000000..0313fe68e --- /dev/null +++ b/conjure-java-core/src/test/java/com/palantir/conjure/java/util/SafetyUtilsTests.java @@ -0,0 +1,77 @@ +/* + * (c) Copyright 2023 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.conjure.java.util; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.palantir.conjure.spec.ExternalReference; +import com.palantir.conjure.spec.FieldDefinition; +import com.palantir.conjure.spec.FieldName; +import com.palantir.conjure.spec.ListType; +import com.palantir.conjure.spec.LogSafety; +import com.palantir.conjure.spec.PrimitiveType; +import com.palantir.conjure.spec.Type; +import com.palantir.conjure.spec.TypeName; +import org.junit.jupiter.api.Test; + +public class SafetyUtilsTests { + + private static final Type SAFE_EXTERNAL = Type.external(ExternalReference.builder() + .externalReference(TypeName.of("Long", "java.lang")) + .fallback(Type.primitive(PrimitiveType.STRING)) + .safety(LogSafety.SAFE) + .build()); + + @Test + public void externalField() { + FieldDefinition field = FieldDefinition.builder() + .fieldName(FieldName.of("testField")) + .type(SAFE_EXTERNAL) + .build(); + assertThat(SafetyUtils.getMaybeExternalSafety(field)).isPresent().get().isEqualTo(LogSafety.SAFE); + } + + @Test + public void nonExternalField() { + FieldDefinition field = FieldDefinition.builder() + .fieldName(FieldName.of("testField")) + .type(Type.primitive(PrimitiveType.STRING)) + .build(); + assertThat(SafetyUtils.getMaybeExternalSafety(field)).isEmpty(); + } + + @Test + public void externalWrapper() { + FieldDefinition field = FieldDefinition.builder() + .fieldName(FieldName.of("testField")) + .type(Type.list(ListType.builder().itemType(SAFE_EXTERNAL).build())) + .build(); + assertThat(SafetyUtils.getMaybeExternalSafety(field)).isPresent().get().isEqualTo(LogSafety.SAFE); + } + + @Test + public void nonExternalWrapper() { + FieldDefinition field = FieldDefinition.builder() + .fieldName(FieldName.of("testField")) + .type(Type.list(ListType.builder() + .itemType(Type.primitive(PrimitiveType.STRING)) + .build())) + .safety(LogSafety.SAFE) + .build(); + assertThat(SafetyUtils.getMaybeExternalSafety(field)).isPresent().get().isEqualTo(LogSafety.SAFE); + } +} diff --git a/conjure-java-core/src/test/resources/example-types.yml b/conjure-java-core/src/test/resources/example-types.yml index 9fd88b30f..d1d5ef1b0 100644 --- a/conjure-java-core/src/test/resources/example-types.yml +++ b/conjure-java-core/src/test/resources/example-types.yml @@ -13,6 +13,11 @@ types: base-type: string external: java: java.lang.String + SafeExternalLong: + base-type: safelong + external: + java: java.lang.Long + safety: safe definitions: default-package: com.palantir.product objects: @@ -25,6 +30,12 @@ types: SafeLongExample: fields: safeLongValue: safelong + SafeExternalLongExample: + fields: + safeExternalLongValue: SafeExternalLong + optionalSafeExternalLong: optional + safeExternalLongList: list + safeExternalLongSet: set RidExample: fields: ridValue: rid @@ -214,6 +225,10 @@ types: unknown: string EmptyUnionTypeExample: union: {} + ExternalLongUnionExample: + docs: A union of a safe long. + union: + safeLong: SafeExternalLong EmptyObjectExample: fields: {} AliasAsMapKeyExample: @@ -272,6 +287,8 @@ types: alias: ExternalLong ExternalLongAliasTwo: alias: ExternalLongAliasOne + SafeExternalLongAlias: + alias: SafeExternalLong OptionalAlias: alias: optional safety: safe diff --git a/conjure-java-core/src/test/resources/external-long-test-service.yml b/conjure-java-core/src/test/resources/external-long-test-service.yml new file mode 100644 index 000000000..4f7627e8c --- /dev/null +++ b/conjure-java-core/src/test/resources/external-long-test-service.yml @@ -0,0 +1,20 @@ +types: + imports: + DangerousLong: + external: + java: java.lang.Long + safety: do-not-log + +services: + ExternalLongTestService: + name: External Long Test Service + package: com.palantir.product + default-auth: header + base-path: /external-long + + endpoints: + testExternalLongArg: + http: POST /test + args: + externalLong: + type: DangerousLong diff --git a/versions.lock b/versions.lock index 291d34c08..c563005fe 100644 --- a/versions.lock +++ b/versions.lock @@ -2,12 +2,12 @@ com.atlassian.commonmark:commonmark:0.12.1 (1 constraints: 36052a3b) com.fasterxml.jackson.core:jackson-annotations:2.14.1 (20 constraints: c04d5f2e) com.fasterxml.jackson.core:jackson-core:2.14.1 (18 constraints: 386dff6a) -com.fasterxml.jackson.core:jackson-databind:2.14.1 (35 constraints: a3918166) +com.fasterxml.jackson.core:jackson-databind:2.14.1 (35 constraints: e59004c2) com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.14.1 (2 constraints: e0339000) com.fasterxml.jackson.dataformat:jackson-dataformat-smile:2.14.1 (1 constraints: 7e1c9da4) -com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.14.1 (3 constraints: 3b25d80f) +com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.14.1 (3 constraints: 3b25da0f) com.fasterxml.jackson.datatype:jackson-datatype-guava:2.14.1 (4 constraints: a550d4cd) -com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.14.1 (4 constraints: 293fe28f) +com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.14.1 (4 constraints: 293fe48f) com.fasterxml.jackson.datatype:jackson-datatype-joda:2.14.1 (2 constraints: 2f2bc4af) com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.14.1 (2 constraints: 2f2bc4af) com.fasterxml.jackson.module:jackson-module-afterburner:2.14.1 (2 constraints: 4b2b57b6) @@ -20,8 +20,8 @@ com.google.guava:guava:31.1-jre (34 constraints: 2b5e25c9) com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava (1 constraints: bd17c918) com.google.j2objc:j2objc-annotations:1.3 (1 constraints: b809eda0) com.palantir.common:streams:2.1.0 (1 constraints: 0505f835) -com.palantir.conjure:conjure-api-objects:4.28.0 (2 constraints: 6322d2e4) -com.palantir.conjure:conjure-generator-common:4.28.0 (2 constraints: 07149683) +com.palantir.conjure:conjure-api-objects:4.36.0 (2 constraints: 612297e4) +com.palantir.conjure:conjure-generator-common:4.36.0 (2 constraints: 05146783) com.palantir.conjure.java.api:errors:2.34.0 (6 constraints: ca6d46c1) com.palantir.conjure.java.api:service-config:2.34.0 (4 constraints: 34488f4c) com.palantir.conjure.java.api:ssl-config:2.34.0 (2 constraints: a425b738) @@ -46,7 +46,7 @@ com.palantir.safe-logging:logger-spi:3.2.0 (2 constraints: 0f1e997a) com.palantir.safe-logging:preconditions:3.2.0 (26 constraints: 9acdf945) com.palantir.safe-logging:safe-logging:3.2.0 (21 constraints: 0f5d9fb6) com.palantir.safethreadlocalrandom:safe-thread-local-random:0.1.0 (3 constraints: 933225f9) -com.palantir.syntactic-paths:syntactic-paths:0.9.0 (2 constraints: 9b137361) +com.palantir.syntactic-paths:syntactic-paths:0.9.0 (2 constraints: 9d137b61) com.palantir.tokens:auth-tokens:3.17.0 (5 constraints: 605156e4) com.palantir.tracing:tracing:6.15.0 (8 constraints: de7caf43) com.palantir.tracing:tracing-api:6.15.0 (6 constraints: 1760c248) @@ -78,7 +78,7 @@ org.jboss.logging:jboss-logging:3.4.1.Final (4 constraints: a24557a8) org.jboss.threads:jboss-threads:3.1.0.Final (2 constraints: 561a9b42) org.jboss.xnio:xnio-api:3.8.7.Final (2 constraints: 771a3146) org.jboss.xnio:xnio-nio:3.8.7.Final (1 constraints: c80dcb30) -org.jetbrains:annotations:24.0.0 (3 constraints: 94287fbe) +org.jetbrains:annotations:24.0.0 (4 constraints: fa396f95) org.mpierce.metrics.reservoir:hdrhistogram-metrics-reservoir:1.1.3 (1 constraints: 0d10f991) org.slf4j:slf4j-api:1.7.36 (27 constraints: 608897a9) org.slf4j:slf4j-simple:1.7.36 (1 constraints: 43054b3b) @@ -105,7 +105,7 @@ com.helger:profiler:1.1.1 (1 constraints: e21053b8) com.netflix.concurrency-limits:concurrency-limits-core:0.2.2 (1 constraints: 7d14fe7b) com.netflix.feign:feign-core:8.18.0 (3 constraints: de3f76e0) com.netflix.feign:feign-jackson:8.18.0 (1 constraints: c718909e) -com.palantir.conjure:conjure-core:4.28.0 (1 constraints: 4005573b) +com.palantir.conjure:conjure-core:4.36.0 (1 constraints: 3f05553b) com.palantir.conjure.java.api:test-utils:2.34.0 (1 constraints: 3b05433b) com.palantir.conjure.java.runtime:conjure-java-annotations:7.40.0 (1 constraints: c1187e9e) com.palantir.conjure.java.runtime:conjure-java-jaxrs-client:7.40.0 (1 constraints: 3d05593b) diff --git a/versions.props b/versions.props index a24e2e557..390d28f0c 100644 --- a/versions.props +++ b/versions.props @@ -7,7 +7,7 @@ com.palantir.common:streams = 2.1.0 com.palantir.conjure.java.api:* = 2.34.0 com.palantir.conjure.java.runtime:* = 7.40.0 com.palantir.conjure.verification:* = 0.19.0 -com.palantir.conjure:* = 4.28.0 +com.palantir.conjure:* = 4.36.0 com.palantir.dialogue:* = 3.75.0 com.palantir.ri:resource-identifier = 2.5.0 com.palantir.safe-logging:* = 3.2.0