Skip to content

Commit

Permalink
Add native support for InputStream responses to dialogue annotations …
Browse files Browse the repository at this point in the history
…processor
  • Loading branch information
aldexis committed Nov 17, 2023
1 parent 31c6b7a commit 6ef619a
Show file tree
Hide file tree
Showing 9 changed files with 218 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.palantir.dialogue.annotations.MapToMultimapParamEncoder;
import com.palantir.dialogue.annotations.Request;
import com.palantir.myservice.example.PutFileRequest.PutFileRequestSerializer;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand All @@ -47,6 +48,9 @@ String greet(
@Request(method = HttpMethod.GET, path = "/greeting", accept = CustomStringDeserializer.class)
ListenableFuture<String> getGreetingAsync();

@Request(method = HttpMethod.GET, path = "/input-stream")
InputStream inputStream();

// No decoders allowed (void method)
// No encoders allowed (RequestBody is pre-encoded)
@Request(method = HttpMethod.PUT, path = "/custom/request")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@

package com.palantir.dialogue.annotations.processor.data;

import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.TypeName;
import java.io.InputStream;
import java.util.Optional;
import org.immutables.value.Value;

Expand All @@ -27,6 +29,11 @@ public interface ReturnType {

TypeName deserializerFactory();

@Value.Default
default boolean isUsingCustomDeserializer() {
return false;
}

TypeName errorDecoder();

String deserializerFieldName();
Expand All @@ -38,4 +45,10 @@ default boolean isVoid() {
TypeName type = asyncInnerType().orElseGet(this::returnType);
return type.box().equals(TypeName.VOID.box());
}

@Value.Derived
default boolean isInputStream() {
TypeName type = asyncInnerType().orElseGet(this::returnType);
return type.equals(ClassName.get(InputStream.class));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public Optional<ReturnType> getReturnType(
.orElseGet(() -> context.getTypeName(ConjureErrorDecoder.class)))
.deserializerFieldName(InstanceVariables.joinCamelCase(endpointName.get(), "Deserializer"))
.asyncInnerType(maybeListenableFutureInnerType.map(TypeName::get))
.isUsingCustomDeserializer(maybeAcceptDeserializerFactory.isPresent())
.build());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.palantir.dialogue.Serializer;
import com.palantir.dialogue.TypeMarker;
import com.palantir.dialogue.annotations.DefaultParameterSerializer;
import com.palantir.dialogue.annotations.ErrorHandlingDeserializer;
import com.palantir.dialogue.annotations.ErrorHandlingDeserializerFactory;
import com.palantir.dialogue.annotations.ErrorHandlingVoidDeserializer;
import com.palantir.dialogue.annotations.ParameterSerializer;
Expand Down Expand Up @@ -166,21 +167,34 @@ private Optional<FieldSpec> deserializer(ReturnType type) {
ParameterizedTypeName deserializerType =
ParameterizedTypeName.get(ClassName.get(Deserializer.class), innerType);

CodeBlock realDeserializer = CodeBlock.of(
CodeBlock deserializer = CodeBlock.of(
"new $T<>(new $T(), new $T()).deserializerFor(new $T<$T>() {})",
ErrorHandlingDeserializerFactory.class,
deserializerFactoryType,
errorDecoderType,
TypeMarker.class,
innerType);
CodeBlock voidDeserializer = CodeBlock.of(
"new $T($L.bodySerDe().emptyBodyDeserializer(), new $T())",
ErrorHandlingVoidDeserializer.class,
serviceDefinition.conjureRuntimeArgName(),
errorDecoderType);

// If no custom deserializer is used, use the runtime-provided deserializers for specific types
if (!type.isUsingCustomDeserializer()) {
if (type.isVoid()) {
deserializer = CodeBlock.of(
"new $T($L.bodySerDe().emptyBodyDeserializer(), new $T())",
ErrorHandlingVoidDeserializer.class,
serviceDefinition.conjureRuntimeArgName(),
errorDecoderType);
} else if (type.isInputStream()) {
deserializer = CodeBlock.of(
"new $T<>($L.bodySerDe().inputStreamDeserializer(), new $T())",
ErrorHandlingDeserializer.class,
serviceDefinition.conjureRuntimeArgName(),
errorDecoderType);
}
}

return Optional.of(FieldSpec.builder(deserializerType, type.deserializerFieldName())
.addModifiers(Modifier.PRIVATE, Modifier.FINAL)
.initializer(type.isVoid() ? voidDeserializer : realDeserializer)
.initializer(deserializer)
.build());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* (c) Copyright 2021 Palantir Technologies Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.palantir.myservice.service;

import com.palantir.dialogue.Response;
import com.palantir.dialogue.annotations.StdDeserializer;
import java.io.InputStream;

public final class CustomInputStreamDeserializer extends StdDeserializer<InputStream> {

public CustomInputStreamDeserializer() {
super("application/octet-stream");
}

@Override
public InputStream deserialize(Response response) {
return response.body();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.palantir.dialogue.Response;
import com.palantir.dialogue.annotations.Request;
import com.palantir.tokens.auth.AuthHeader;
import java.io.InputStream;
import java.util.List;
import java.util.Optional;
import java.util.OptionalDouble;
Expand All @@ -47,6 +48,12 @@ String greet(
@Request(method = HttpMethod.GET, path = "/greeting", accept = CustomStringDeserializer.class)
ListenableFuture<String> getGreetingAsync();

@Request(method = HttpMethod.GET, path = "/input-stream")
InputStream inputStream();

@Request(method = HttpMethod.GET, path = "/input-stream-custom", accept = CustomInputStreamDeserializer.class)
InputStream customInputStream();

// No decoders allowed (void method)
// No encoders allowed (RequestBody is pre-encoded)
@Request(method = HttpMethod.PUT, path = "/custom/request")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ import com.palantir.dialogue.TypeMarker;
import com.palantir.dialogue.UrlBuilder;
import com.palantir.dialogue.annotations.ConjureErrorDecoder;
import com.palantir.dialogue.annotations.DefaultParameterSerializer;
import com.palantir.dialogue.annotations.ErrorHandlingDeserializer;
import com.palantir.dialogue.annotations.ErrorHandlingDeserializerFactory;
import com.palantir.dialogue.annotations.ErrorHandlingVoidDeserializer;
import com.palantir.dialogue.annotations.Json;
import com.palantir.dialogue.annotations.ParameterSerializer;
import com.palantir.dialogue.annotations.ResponseDeserializer;
import com.palantir.tokens.auth.AuthHeader;
import java.io.InputStream;
import java.lang.Override;
import java.lang.String;
import java.lang.Void;
Expand Down Expand Up @@ -57,6 +59,19 @@ public final class MyServiceDialogueServiceFactory implements DialogueServiceFac
new CustomStringDeserializer(), new ConjureErrorDecoder())
.deserializerFor(new TypeMarker<String>() {});

private final EndpointChannel inputStreamChannel = endpointChannelFactory.endpoint(Endpoints.inputStream);

private final Deserializer<InputStream> inputStreamDeserializer = new ErrorHandlingDeserializer<>(
runtime.bodySerDe().inputStreamDeserializer(), new ConjureErrorDecoder());

private final EndpointChannel customInputStreamChannel =
endpointChannelFactory.endpoint(Endpoints.customInputStream);

private final Deserializer<InputStream> customInputStreamDeserializer =
new ErrorHandlingDeserializerFactory<>(
new CustomInputStreamDeserializer(), new ConjureErrorDecoder())
.deserializerFor(new TypeMarker<InputStream>() {});

private final EndpointChannel customRequestChannel =
endpointChannelFactory.endpoint(Endpoints.customRequest);

Expand Down Expand Up @@ -110,6 +125,19 @@ public final class MyServiceDialogueServiceFactory implements DialogueServiceFac
return runtime.clients().call(getGreetingAsyncChannel, _request.build(), getGreetingAsyncDeserializer);
}

@Override
public InputStream inputStream() {
Request.Builder _request = Request.builder();
return runtime.clients().callBlocking(inputStreamChannel, _request.build(), inputStreamDeserializer);
}

@Override
public InputStream customInputStream() {
Request.Builder _request = Request.builder();
return runtime.clients()
.callBlocking(customInputStreamChannel, _request.build(), customInputStreamDeserializer);
}

@Override
public void customRequest(EmptyRequestBody requestBody) {
Request.Builder _request = Request.builder();
Expand Down Expand Up @@ -275,6 +303,66 @@ public final class MyServiceDialogueServiceFactory implements DialogueServiceFac
}
},

inputStream {
private final PathTemplate pathTemplate =
PathTemplate.builder().fixed("input-stream").build();

@Override
public void renderPath(ListMultimap<String, String> params, UrlBuilder url) {
pathTemplate.fill(params, url);
}

@Override
public HttpMethod httpMethod() {
return HttpMethod.GET;
}

@Override
public String serviceName() {
return "MyService";
}

@Override
public String endpointName() {
return "inputStream";
}

@Override
public String version() {
return VERSION;
}
},

customInputStream {
private final PathTemplate pathTemplate =
PathTemplate.builder().fixed("input-stream-custom").build();

@Override
public void renderPath(ListMultimap<String, String> params, UrlBuilder url) {
pathTemplate.fill(params, url);
}

@Override
public HttpMethod httpMethod() {
return HttpMethod.GET;
}

@Override
public String serviceName() {
return "MyService";
}

@Override
public String endpointName() {
return "customInputStream";
}

@Override
public String version() {
return VERSION;
}
},

customRequest {
private final PathTemplate pathTemplate =
PathTemplate.builder().fixed("custom").fixed("request").build();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* (c) Copyright 2021 Palantir Technologies Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.palantir.dialogue.annotations;

import com.palantir.dialogue.Deserializer;
import com.palantir.dialogue.Response;
import com.palantir.logsafe.Preconditions;
import java.util.Optional;

public class ErrorHandlingDeserializer<T> implements Deserializer<T> {

private final Deserializer<T> delegate;
private final ErrorDecoder errorDecoder;

public ErrorHandlingDeserializer(Deserializer<T> delegate, ErrorDecoder errorDecoder) {
this.delegate = Preconditions.checkNotNull(delegate, "delegate");
this.errorDecoder = Preconditions.checkNotNull(errorDecoder, "errorDecoder");
}

@Override
public final T deserialize(Response response) {
try (response) {
if (errorDecoder.isError(response)) {
throw errorDecoder.decode(response);
} else {
return delegate.deserialize(response);
}
}
}

@Override
public final Optional<String> accepts() {
return delegate.accepts();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,33 +17,10 @@
package com.palantir.dialogue.annotations;

import com.palantir.dialogue.Deserializer;
import com.palantir.dialogue.Response;
import com.palantir.logsafe.Preconditions;
import java.util.Optional;

public final class ErrorHandlingVoidDeserializer implements Deserializer<Void> {

private final Deserializer<Void> delegate;
private final ErrorDecoder errorDecoder;
public final class ErrorHandlingVoidDeserializer extends ErrorHandlingDeserializer<Void> {

public ErrorHandlingVoidDeserializer(Deserializer<Void> delegate, ErrorDecoder errorDecoder) {
this.delegate = Preconditions.checkNotNull(delegate, "delegate");
this.errorDecoder = Preconditions.checkNotNull(errorDecoder, "errorDecoder");
}

@Override
public Void deserialize(Response response) {
try (response) {
if (errorDecoder.isError(response)) {
throw errorDecoder.decode(response);
} else {
return delegate.deserialize(response);
}
}
}

@Override
public Optional<String> accepts() {
return delegate.accepts();
super(delegate, errorDecoder);
}
}

0 comments on commit 6ef619a

Please sign in to comment.