From 113c3eb294ce67323d9fc1b6099118a56697d11f Mon Sep 17 00:00:00 2001 From: OZGCloud <ozgcloud@mgm-tp.com> Date: Thu, 17 Aug 2023 14:47:48 +0200 Subject: [PATCH] build and add call context --- .../common/callcontext/CallContext.java | 24 ++++++ .../DefaultOzgCloudCallContextProvider.java | 33 +++++++ ...gCloudCallContextAttachingInterceptor.java | 86 +++++++++++++++++++ .../OzgCloudCallContextProvider.java | 6 ++ ...e.java => GrpcOzgCloudVorgangService.java} | 2 +- ...va => GrpcOzgCloudVorgangServiceTest.java} | 4 +- .../de/ozgcloud/apilib/demo/DemoRunner.java | 2 +- .../OzgCloudClientAutoConfiguration.java | 18 +++- 8 files changed, 168 insertions(+), 7 deletions(-) create mode 100644 api-lib-core/src/main/java/de/ozgcloud/apilib/common/callcontext/CallContext.java create mode 100644 api-lib-core/src/main/java/de/ozgcloud/apilib/common/callcontext/DefaultOzgCloudCallContextProvider.java create mode 100644 api-lib-core/src/main/java/de/ozgcloud/apilib/common/callcontext/OzgCloudCallContextAttachingInterceptor.java create mode 100644 api-lib-core/src/main/java/de/ozgcloud/apilib/common/callcontext/OzgCloudCallContextProvider.java rename api-lib-core/src/main/java/de/ozgcloud/apilib/vorgang/grpc/{GrpcVorgangService.java => GrpcOzgCloudVorgangService.java} (96%) rename api-lib-core/src/test/java/de/ozgcloud/apilib/vorgang/grpc/{GrpcVorgangServiceTest.java => GrpcOzgCloudVorgangServiceTest.java} (97%) diff --git a/api-lib-core/src/main/java/de/ozgcloud/apilib/common/callcontext/CallContext.java b/api-lib-core/src/main/java/de/ozgcloud/apilib/common/callcontext/CallContext.java new file mode 100644 index 0000000..449032b --- /dev/null +++ b/api-lib-core/src/main/java/de/ozgcloud/apilib/common/callcontext/CallContext.java @@ -0,0 +1,24 @@ +package de.ozgcloud.apilib.common.callcontext; + +import java.util.Collection; +import java.util.UUID; + +import de.ozgcloud.apilib.user.OzgCloudUserId; +import lombok.Builder; +import lombok.Getter; +import lombok.Singular; + +@Builder +@Getter +public class CallContext { + + private String clientName; + private OzgCloudUserId userId; + + @Builder.Default + private String requestId = UUID.randomUUID().toString(); + @Builder.Default + private boolean accessLimited = false; + @Singular + private Collection<String> accessLimitedToOrgaIds; +} diff --git a/api-lib-core/src/main/java/de/ozgcloud/apilib/common/callcontext/DefaultOzgCloudCallContextProvider.java b/api-lib-core/src/main/java/de/ozgcloud/apilib/common/callcontext/DefaultOzgCloudCallContextProvider.java new file mode 100644 index 0000000..2528822 --- /dev/null +++ b/api-lib-core/src/main/java/de/ozgcloud/apilib/common/callcontext/DefaultOzgCloudCallContextProvider.java @@ -0,0 +1,33 @@ +package de.ozgcloud.apilib.common.callcontext; + +import java.util.Optional; +import java.util.UUID; +import java.util.logging.Level; + +import org.springframework.context.ApplicationContext; + +import lombok.RequiredArgsConstructor; +import lombok.extern.java.Log; + +@Log +@RequiredArgsConstructor +public class DefaultOzgCloudCallContextProvider implements OzgCloudCallContextProvider { + + private final ApplicationContext context; + + @Override + public CallContext provideContext() { + return CallContext.builder() + .clientName(getClientName()) + .requestId(UUID.randomUUID().toString()) + .build(); + } + + private String getClientName() { + return Optional.ofNullable(context.getId()).orElseGet(() -> { + LOG.log(Level.WARNING, "No Client name given. Please configure 'spring.application.name'."); + return "unkown_client"; + }); + + } +} diff --git a/api-lib-core/src/main/java/de/ozgcloud/apilib/common/callcontext/OzgCloudCallContextAttachingInterceptor.java b/api-lib-core/src/main/java/de/ozgcloud/apilib/common/callcontext/OzgCloudCallContextAttachingInterceptor.java new file mode 100644 index 0000000..50a5ddc --- /dev/null +++ b/api-lib-core/src/main/java/de/ozgcloud/apilib/common/callcontext/OzgCloudCallContextAttachingInterceptor.java @@ -0,0 +1,86 @@ +package de.ozgcloud.apilib.common.callcontext; + +import static de.itvsh.kop.common.grpc.GrpcUtil.*; + +import java.util.Optional; +import java.util.UUID; + +import org.apache.commons.lang3.StringUtils; + +import de.ozgcloud.apilib.user.OzgCloudUserId; +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.ClientCall; +import io.grpc.ClientInterceptor; +import io.grpc.ForwardingClientCall.SimpleForwardingClientCall; +import io.grpc.Metadata; +import io.grpc.MethodDescriptor; +import lombok.RequiredArgsConstructor; +import net.devh.boot.grpc.client.interceptor.GrpcGlobalClientInterceptor; + +@GrpcGlobalClientInterceptor +@RequiredArgsConstructor +public class OzgCloudCallContextAttachingInterceptor implements ClientInterceptor { + + static final String KEY_USER_ID = "USER_ID-bin"; + static final String KEY_CLIENT_NAME = "CLIENT_NAME-bin"; + static final String KEY_REQUEST_ID = "REQUEST_ID-bin"; + static final String KEY_ACCESS_LIMITED_ORGAID = "ACCESS_LIMITED_TO_ORGANISATORISCHEEINHEITENID-bin"; + static final String KEY_ACCESS_LIMITED = "ACCESS_LIMITED-bin"; + + private final OzgCloudCallContextProvider callContextProvider; + + @Override + public <A, B> ClientCall<A, B> interceptCall(MethodDescriptor<A, B> method, CallOptions callOptions, Channel next) { + return new CallContextAttachingClientCall<>(next.newCall(method, callOptions)); + } + + final class CallContextAttachingClientCall<A, B> extends SimpleForwardingClientCall<A, B> { + + protected CallContextAttachingClientCall(ClientCall<A, B> delegate) { + super(delegate); + } + + @Override + public void start(Listener<B> responseListener, Metadata headers) { + headers.merge(buildCallContextMetadata()); + super.start(responseListener, headers); + } + + private Metadata buildCallContextMetadata() { + var ctxt = callContextProvider.provideContext(); + var metadata = new Metadata(); + + addClientName(ctxt, metadata); + addRequestId(ctxt, metadata); + Optional.ofNullable(ctxt.getUserId()) + .map(OzgCloudUserId::toString) + .ifPresent(userId -> addToMetadata(metadata, KEY_USER_ID, userId)); + Optional.ofNullable(ctxt.getAccessLimitedToOrgaIds()) + .ifPresent(ids -> ids.stream().forEach(id -> addToMetadata(metadata, KEY_ACCESS_LIMITED_ORGAID, id))); + Optional.ofNullable(ctxt.isAccessLimited()).map(Boolean::valueOf) + .ifPresent(limited -> addToMetadata(metadata, KEY_ACCESS_LIMITED, limited.toString())); + + return metadata; + } + + private void addRequestId(CallContext ctxt, Metadata metadata) { + Optional.ofNullable(ctxt.getRequestId()).ifPresentOrElse(reqId -> metadata.put(createKeyOf(KEY_REQUEST_ID), reqId.getBytes()), + () -> addToMetadata(metadata, KEY_REQUEST_ID, UUID.randomUUID().toString())); + } + + private void addClientName(CallContext ctxt, Metadata metadata) { + Optional.ofNullable(ctxt.getClientName()) + .filter(StringUtils::isNoneBlank) + .ifPresentOrElse(clientName -> addToMetadata(metadata, KEY_CLIENT_NAME, clientName), + () -> { + throw new IllegalStateException("CLIENT_NAME must not be blank"); + }); + } + + private void addToMetadata(Metadata metadata, String key, String value) { + metadata.put(createKeyOf(key), value.getBytes()); + } + + } +} diff --git a/api-lib-core/src/main/java/de/ozgcloud/apilib/common/callcontext/OzgCloudCallContextProvider.java b/api-lib-core/src/main/java/de/ozgcloud/apilib/common/callcontext/OzgCloudCallContextProvider.java new file mode 100644 index 0000000..2bd4a70 --- /dev/null +++ b/api-lib-core/src/main/java/de/ozgcloud/apilib/common/callcontext/OzgCloudCallContextProvider.java @@ -0,0 +1,6 @@ +package de.ozgcloud.apilib.common.callcontext; + +public interface OzgCloudCallContextProvider { + + public CallContext provideContext(); +} diff --git a/api-lib-core/src/main/java/de/ozgcloud/apilib/vorgang/grpc/GrpcVorgangService.java b/api-lib-core/src/main/java/de/ozgcloud/apilib/vorgang/grpc/GrpcOzgCloudVorgangService.java similarity index 96% rename from api-lib-core/src/main/java/de/ozgcloud/apilib/vorgang/grpc/GrpcVorgangService.java rename to api-lib-core/src/main/java/de/ozgcloud/apilib/vorgang/grpc/GrpcOzgCloudVorgangService.java index a0a5982..bbcbcf7 100644 --- a/api-lib-core/src/main/java/de/ozgcloud/apilib/vorgang/grpc/GrpcVorgangService.java +++ b/api-lib-core/src/main/java/de/ozgcloud/apilib/vorgang/grpc/GrpcOzgCloudVorgangService.java @@ -22,7 +22,7 @@ import net.devh.boot.grpc.client.inject.GrpcClient; @Service @ConditionalOnProperty("ozgcloud.vorgang-manager.address") @RequiredArgsConstructor -public class GrpcVorgangService implements OzgCloudVorgangService { +public class GrpcOzgCloudVorgangService implements OzgCloudVorgangService { @GrpcClient("vorgang-manager") private final VorgangServiceBlockingStub vorgangServiceStub; diff --git a/api-lib-core/src/test/java/de/ozgcloud/apilib/vorgang/grpc/GrpcVorgangServiceTest.java b/api-lib-core/src/test/java/de/ozgcloud/apilib/vorgang/grpc/GrpcOzgCloudVorgangServiceTest.java similarity index 97% rename from api-lib-core/src/test/java/de/ozgcloud/apilib/vorgang/grpc/GrpcVorgangServiceTest.java rename to api-lib-core/src/test/java/de/ozgcloud/apilib/vorgang/grpc/GrpcOzgCloudVorgangServiceTest.java index 0f9cc3c..ad87c0e 100644 --- a/api-lib-core/src/test/java/de/ozgcloud/apilib/vorgang/grpc/GrpcVorgangServiceTest.java +++ b/api-lib-core/src/test/java/de/ozgcloud/apilib/vorgang/grpc/GrpcOzgCloudVorgangServiceTest.java @@ -21,10 +21,10 @@ import de.ozgcloud.apilib.vorgang.OzgCloudVorgangStubTestFactory; import de.ozgcloud.apilib.vorgang.OzgCloudVorgangTestFactory; import de.ozgcloud.apilib.vorgang.Page; -class GrpcVorgangServiceTest { +class GrpcOzgCloudVorgangServiceTest { @InjectMocks - private GrpcVorgangService service; + private GrpcOzgCloudVorgangService service; @Mock private VorgangServiceBlockingStub stub; diff --git a/api-lib-demo/src/main/java/de/ozgcloud/apilib/demo/DemoRunner.java b/api-lib-demo/src/main/java/de/ozgcloud/apilib/demo/DemoRunner.java index ba3b61d..b3709fc 100644 --- a/api-lib-demo/src/main/java/de/ozgcloud/apilib/demo/DemoRunner.java +++ b/api-lib-demo/src/main/java/de/ozgcloud/apilib/demo/DemoRunner.java @@ -20,7 +20,7 @@ class DemoRunner implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEvent(ContextRefreshedEvent event) { - System.out.println(vorgangService.getById(OzgCloudVorgangId.from("647885a50b105b1e4995378e"))); + System.out.println(vorgangService.getById(OzgCloudVorgangId.from("64bfd6597a08cf6e8a5185c6"))); System.out.println(fileService.getFile(OzgCloudFileId.from("630363d5b5816c0d8efd6f19"))); diff --git a/ozg-cloud-spring-boot-starter/src/main/java/de/ozgcloud/client/autoconfigure/OzgCloudClientAutoConfiguration.java b/ozg-cloud-spring-boot-starter/src/main/java/de/ozgcloud/client/autoconfigure/OzgCloudClientAutoConfiguration.java index b27610b..41f4957 100644 --- a/ozg-cloud-spring-boot-starter/src/main/java/de/ozgcloud/client/autoconfigure/OzgCloudClientAutoConfiguration.java +++ b/ozg-cloud-spring-boot-starter/src/main/java/de/ozgcloud/client/autoconfigure/OzgCloudClientAutoConfiguration.java @@ -4,15 +4,20 @@ import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Import; +import de.ozgcloud.apilib.common.callcontext.DefaultOzgCloudCallContextProvider; +import de.ozgcloud.apilib.common.callcontext.OzgCloudCallContextAttachingInterceptor; +import de.ozgcloud.apilib.common.callcontext.OzgCloudCallContextProvider; import de.ozgcloud.apilib.file.dummy.DummyOzgCloudFileService; import de.ozgcloud.apilib.file.grpc.GrpcOzgCloudFileService; import de.ozgcloud.apilib.vorgang.dummy.DummyVorgangService; -import de.ozgcloud.apilib.vorgang.grpc.GrpcVorgangService; +import de.ozgcloud.apilib.vorgang.grpc.GrpcOzgCloudVorgangService; import net.devh.boot.grpc.client.autoconfigure.GrpcClientAutoConfiguration; import net.devh.boot.grpc.client.config.GrpcChannelProperties; import net.devh.boot.grpc.client.config.GrpcChannelsProperties; @@ -20,8 +25,9 @@ import net.devh.boot.grpc.client.config.GrpcChannelsProperties; @AutoConfiguration(before = GrpcClientAutoConfiguration.class) @ComponentScan("de.ozgcloud.client.autoconfigure") @Import({ - GrpcVorgangService.class, DummyVorgangService.class, - GrpcOzgCloudFileService.class, DummyOzgCloudFileService.class + GrpcOzgCloudVorgangService.class, DummyVorgangService.class, + GrpcOzgCloudFileService.class, DummyOzgCloudFileService.class, + OzgCloudCallContextAttachingInterceptor.class }) public class OzgCloudClientAutoConfiguration { @@ -60,4 +66,10 @@ public class OzgCloudClientAutoConfiguration { clientMap.put(CLIENT_NAME_FILE_MANAGER, channelProps); } + + @Bean + @ConditionalOnMissingBean + OzgCloudCallContextProvider callContextProvider(ApplicationContext ctxt) { + return new DefaultOzgCloudCallContextProvider(ctxt); + } } -- GitLab