From e535c3dc2f384b578d0348b85328b0d11a0f3f72 Mon Sep 17 00:00:00 2001 From: willjsw Date: Thu, 12 Jun 2025 15:12:30 +0900 Subject: [PATCH 01/20] =?UTF-8?q?KW-659/feat:=20Mediator,=20Hospital=20Web?= =?UTF-8?q?Client=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + .../com/doubleo/didagent/agent/AcapyAgent.java | 3 --- .../didagent/infra/config/PropertiesConfig.java | 15 +++++++++++++++ .../infra/config/acapy/AcapyProperties.java | 11 +++++++++++ .../infra/config/hospital/HospitalProperties.java | 6 ++++++ .../infra/config/mediator/MediatorProperties.java | 6 ++++++ 6 files changed, 39 insertions(+), 3 deletions(-) delete mode 100644 src/main/java/com/doubleo/didagent/agent/AcapyAgent.java create mode 100644 src/main/java/com/doubleo/didagent/infra/config/PropertiesConfig.java create mode 100644 src/main/java/com/doubleo/didagent/infra/config/acapy/AcapyProperties.java create mode 100644 src/main/java/com/doubleo/didagent/infra/config/hospital/HospitalProperties.java create mode 100644 src/main/java/com/doubleo/didagent/infra/config/mediator/MediatorProperties.java diff --git a/build.gradle b/build.gradle index 572a481..274035e 100644 --- a/build.gradle +++ b/build.gradle @@ -40,6 +40,7 @@ ext { dependencies { // implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-webflux' // //config // implementation 'org.springframework.cloud:spring-cloud-starter-config' // implementation 'org.springframework.cloud:spring-cloud-starter-bus-amqp' diff --git a/src/main/java/com/doubleo/didagent/agent/AcapyAgent.java b/src/main/java/com/doubleo/didagent/agent/AcapyAgent.java deleted file mode 100644 index 01205a2..0000000 --- a/src/main/java/com/doubleo/didagent/agent/AcapyAgent.java +++ /dev/null @@ -1,3 +0,0 @@ -package com.doubleo.didagent.agent; - -public class AcapyAgent {} diff --git a/src/main/java/com/doubleo/didagent/infra/config/PropertiesConfig.java b/src/main/java/com/doubleo/didagent/infra/config/PropertiesConfig.java new file mode 100644 index 0000000..43be8ee --- /dev/null +++ b/src/main/java/com/doubleo/didagent/infra/config/PropertiesConfig.java @@ -0,0 +1,15 @@ +package com.doubleo.didagent.infra.config; + +import com.doubleo.didagent.infra.config.acapy.AcapyProperties; +import com.doubleo.didagent.infra.config.hospital.HospitalProperties; +import com.doubleo.didagent.infra.config.mediator.MediatorProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@EnableConfigurationProperties({ + MediatorProperties.class, + HospitalProperties.class, + AcapyProperties.class +}) +@Configuration +public class PropertiesConfig {} diff --git a/src/main/java/com/doubleo/didagent/infra/config/acapy/AcapyProperties.java b/src/main/java/com/doubleo/didagent/infra/config/acapy/AcapyProperties.java new file mode 100644 index 0000000..bd76c23 --- /dev/null +++ b/src/main/java/com/doubleo/didagent/infra/config/acapy/AcapyProperties.java @@ -0,0 +1,11 @@ +package com.doubleo.didagent.infra.config.acapy; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "acapy") +public record AcapyProperties( + String createInvitation, + String checkConnection, + String createDid, + String postPublicDid, + String issueVc) {} diff --git a/src/main/java/com/doubleo/didagent/infra/config/hospital/HospitalProperties.java b/src/main/java/com/doubleo/didagent/infra/config/hospital/HospitalProperties.java new file mode 100644 index 0000000..c2b7866 --- /dev/null +++ b/src/main/java/com/doubleo/didagent/infra/config/hospital/HospitalProperties.java @@ -0,0 +1,6 @@ +package com.doubleo.didagent.infra.config.hospital; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "hospital") +public record HospitalProperties(String adminUrl) {} diff --git a/src/main/java/com/doubleo/didagent/infra/config/mediator/MediatorProperties.java b/src/main/java/com/doubleo/didagent/infra/config/mediator/MediatorProperties.java new file mode 100644 index 0000000..2dec40a --- /dev/null +++ b/src/main/java/com/doubleo/didagent/infra/config/mediator/MediatorProperties.java @@ -0,0 +1,6 @@ +package com.doubleo.didagent.infra.config.mediator; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "mediator") +public record MediatorProperties(String adminUrl) {} From 82a623eea34fce70aa4c63b084f5c572fcf9ae1f Mon Sep 17 00:00:00 2001 From: willjsw Date: Fri, 13 Jun 2025 08:54:43 +0900 Subject: [PATCH 02/20] =?UTF-8?q?KW-659/feat:=20redis=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 9 +++- .../controller/AcapyWebhookController.java | 27 ++++++++++++ .../HospitalInvitationCreateRequest.java | 32 ++++++++++++++ .../grpc/server/AcapyGrpcServiceImpl.java | 43 +++++++++++++++++++ 4 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/doubleo/didagent/controller/AcapyWebhookController.java create mode 100644 src/main/java/com/doubleo/didagent/dto/request/hospital/HospitalInvitationCreateRequest.java create mode 100644 src/main/java/com/doubleo/didagent/grpc/server/AcapyGrpcServiceImpl.java diff --git a/build.gradle b/build.gradle index 274035e..df696cd 100644 --- a/build.gradle +++ b/build.gradle @@ -51,6 +51,8 @@ dependencies { compileOnly 'org.projectlombok:lombok' //mysql // implementation 'mysql:mysql-connector-java:8.0.33' + //redis + implementation 'org.springframework.boot:spring-boot-starter-data-redis' //actuator implementation 'org.springframework.boot:spring-boot-starter-actuator' @@ -61,7 +63,8 @@ dependencies { implementation 'org.json:json:20240303' // grpc interface - implementation "com.doubleo.grpc:tenant-interface:0.2.5" + implementation "com.doubleo.grpc:tenant-interface:0.2.9" + implementation "com.doubleo.grpc:acapy-interface:0.2.9" // gRPC 런타임 implementation "io.grpc:grpc-netty:1.63.0" @@ -72,6 +75,10 @@ dependencies { implementation 'com.google.protobuf:protobuf-java:3.24.4' implementation 'net.devh:grpc-spring-boot-starter:3.1.0.RELEASE' + // json + implementation 'com.fasterxml.jackson.core:jackson-core:2.15.2' + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.15.2' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' diff --git a/src/main/java/com/doubleo/didagent/controller/AcapyWebhookController.java b/src/main/java/com/doubleo/didagent/controller/AcapyWebhookController.java new file mode 100644 index 0000000..9bebdab --- /dev/null +++ b/src/main/java/com/doubleo/didagent/controller/AcapyWebhookController.java @@ -0,0 +1,27 @@ +package com.doubleo.didagent.controller; + +import jakarta.servlet.http.HttpServletRequest; +import java.util.Map; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/webhooks") +@Slf4j +public class AcapyWebhookController { + + @PostMapping("/**") + @ResponseStatus(HttpStatus.OK) + public void receiveAnyWebhook( + HttpServletRequest req, + @RequestBody Map payload, + @RequestHeader Map headers) { + String path = req.getRequestURI(); + log.info("=== Webhook Path: {} ===", path); + log.info("--- Headers ---"); + headers.forEach((k, v) -> log.info("{}: {}", k, v)); + log.info("--- Payload ---"); + log.info("{}", payload); + } +} diff --git a/src/main/java/com/doubleo/didagent/dto/request/hospital/HospitalInvitationCreateRequest.java b/src/main/java/com/doubleo/didagent/dto/request/hospital/HospitalInvitationCreateRequest.java new file mode 100644 index 0000000..2574d46 --- /dev/null +++ b/src/main/java/com/doubleo/didagent/dto/request/hospital/HospitalInvitationCreateRequest.java @@ -0,0 +1,32 @@ +package com.doubleo.didagent.dto.request.hospital; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +public record HospitalInvitationCreateRequest( + @JsonProperty("alias") String alias, + @JsonProperty("handshake_protocols") List handshakeProtocols, + @JsonProperty("goal_code") String goalCode, + @JsonProperty("my_label") String myLabel, + @JsonProperty("accept") List accept, + @JsonProperty("use_did_method") String useDidMethod, + @JsonProperty("multi_use") boolean multiUse) { + public static HospitalInvitationCreateRequest create(String tenantId) { + List handshakeProtocols = new ArrayList<>(); + List accept = new ArrayList<>(); + + handshakeProtocols.add("https://didcomm.org/didexchange/1.0"); + accept.add("didcomm/aip2;env=rfc19"); + + return new HospitalInvitationCreateRequest( + tenantId + ":" + LocalDateTime.now(), + handshakeProtocols, + "vc-issue", + tenantId, + accept, + "did:peer:2", + true); + } +} diff --git a/src/main/java/com/doubleo/didagent/grpc/server/AcapyGrpcServiceImpl.java b/src/main/java/com/doubleo/didagent/grpc/server/AcapyGrpcServiceImpl.java new file mode 100644 index 0000000..3e04892 --- /dev/null +++ b/src/main/java/com/doubleo/didagent/grpc/server/AcapyGrpcServiceImpl.java @@ -0,0 +1,43 @@ +package com.doubleo.didagent.grpc.server; + +import com.doubleo.didagent.agent.HospitalAgent; +import com.doubleo.didagent.dto.request.hospital.HospitalInvitationCreateRequest; +import com.doubleo.didagent.dto.response.hospital.HospitalInvitationCreateResponse; +import com.doubleo.didagent.grpc.client.HospitalTenantClient; +import io.grpc.stub.StreamObserver; +import lombok.RequiredArgsConstructor; +import net.devh.boot.grpc.server.service.GrpcService; + +@GrpcService +@RequiredArgsConstructor +public class AcapyGrpcServiceImpl extends AcapyServiceGrpc.AcapyServiceImplBase { + + private final HospitalAgent hospitalAgent; + private final HospitalTenantClient hospitalTenantClient; + + @Override + public void getPassConnectionId( + PassConnectionIdRequest request, + StreamObserver responseObserver) { + + HospitalInvitationCreateResponse invitation = + hospitalAgent + .createHospitalInvitation( + HospitalInvitationCreateRequest.create(request.getTenantId()), + "token" + // ,hospitalTenantClient.getTenantId() + ) + .block(); + invitation.invitationUrl(); + hospitalAgent.getHospitalConnectionInfoByOobId(); + + hospitalAgent.createHospitalDid(); + hospitalAgent.postHospitalDid(); + hospitalAgent.issueHospitalVc(); + + PassConnectionIdResponse response = + PassConnectionIdResponse.newBuilder().setPassConnectionId(sb.toString()).build(); + responseObserver.onNext(response); + responseObserver.onCompleted(); + } +} From f5df4958784e6034eb78167ea989852483e568d1 Mon Sep 17 00:00:00 2001 From: willjsw Date: Mon, 16 Jun 2025 12:00:38 +0900 Subject: [PATCH 03/20] =?UTF-8?q?KW-659/feat:=20grpc=20pass-interface=20&?= =?UTF-8?q?=20acapy-interface=20v0.3.3=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index df696cd..be45cf3 100644 --- a/build.gradle +++ b/build.gradle @@ -63,8 +63,9 @@ dependencies { implementation 'org.json:json:20240303' // grpc interface - implementation "com.doubleo.grpc:tenant-interface:0.2.9" - implementation "com.doubleo.grpc:acapy-interface:0.2.9" + implementation "com.doubleo.grpc:tenant-interface:0.3.3" + implementation "com.doubleo.grpc:acapy-interface:0.3.3" + implementation "com.doubleo.grpc:pass-interface:0.3.3" // gRPC 런타임 implementation "io.grpc:grpc-netty:1.63.0" From 4a6c4af1b48561ee2f1dc6aa4d468e261b9926fa Mon Sep 17 00:00:00 2001 From: willjsw Date: Mon, 16 Jun 2025 12:10:19 +0900 Subject: [PATCH 04/20] =?UTF-8?q?KW-659/feat:=20webflux=20yml=20config=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 62328f4..493bdd3 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,6 +1,17 @@ spring: application: name: did-agent + data: + redis: + host: localhost + port: 6379 + password: + task: + execution: + pool: + core-size: 10 + max-size: 20 + queue-capacity: 100 server: port: 8080 management: From 0c484bf6fbf4ed93e5c12d9220ed6b91d5f51448 Mon Sep 17 00:00:00 2001 From: willjsw Date: Mon, 16 Jun 2025 12:12:18 +0900 Subject: [PATCH 05/20] =?UTF-8?q?KW-659/feat:=20redis=20property=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infra/config/PropertiesConfig.java | 2 ++ .../infra/config/redis/RedisConfig.java | 35 +++++++++++++++++++ .../infra/config/redis/RedisProperties.java | 6 ++++ 3 files changed, 43 insertions(+) create mode 100644 src/main/java/com/doubleo/didagent/infra/config/redis/RedisConfig.java create mode 100644 src/main/java/com/doubleo/didagent/infra/config/redis/RedisProperties.java diff --git a/src/main/java/com/doubleo/didagent/infra/config/PropertiesConfig.java b/src/main/java/com/doubleo/didagent/infra/config/PropertiesConfig.java index 43be8ee..86f1984 100644 --- a/src/main/java/com/doubleo/didagent/infra/config/PropertiesConfig.java +++ b/src/main/java/com/doubleo/didagent/infra/config/PropertiesConfig.java @@ -3,12 +3,14 @@ import com.doubleo.didagent.infra.config.acapy.AcapyProperties; import com.doubleo.didagent.infra.config.hospital.HospitalProperties; import com.doubleo.didagent.infra.config.mediator.MediatorProperties; +import com.doubleo.didagent.infra.config.redis.RedisProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; @EnableConfigurationProperties({ MediatorProperties.class, HospitalProperties.class, + RedisProperties.class, AcapyProperties.class }) @Configuration diff --git a/src/main/java/com/doubleo/didagent/infra/config/redis/RedisConfig.java b/src/main/java/com/doubleo/didagent/infra/config/redis/RedisConfig.java new file mode 100644 index 0000000..96f2129 --- /dev/null +++ b/src/main/java/com/doubleo/didagent/infra/config/redis/RedisConfig.java @@ -0,0 +1,35 @@ +package com.doubleo.didagent.infra.config.redis; + +import java.time.Duration; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; + +@Configuration +@RequiredArgsConstructor +public class RedisConfig { + + private final RedisProperties redisProperties; + + @Bean + public RedisConnectionFactory redisConnectionFactory() { + RedisStandaloneConfiguration redisStandaloneConfig = + new RedisStandaloneConfiguration(redisProperties.host(), redisProperties.port()); + + if (!redisProperties.password().isBlank()) { + redisStandaloneConfig.setPassword(redisProperties.password()); + } + + LettuceClientConfiguration lettuceClientConfig = + LettuceClientConfiguration.builder() + .commandTimeout(Duration.ofSeconds(1)) + .shutdownTimeout(Duration.ZERO) + .build(); + + return new LettuceConnectionFactory(redisStandaloneConfig, lettuceClientConfig); + } +} diff --git a/src/main/java/com/doubleo/didagent/infra/config/redis/RedisProperties.java b/src/main/java/com/doubleo/didagent/infra/config/redis/RedisProperties.java new file mode 100644 index 0000000..d57088b --- /dev/null +++ b/src/main/java/com/doubleo/didagent/infra/config/redis/RedisProperties.java @@ -0,0 +1,6 @@ +package com.doubleo.didagent.infra.config.redis; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "spring.data.redis") +public record RedisProperties(String host, int port, String password) {} From 05fd3ff8b1fbec9ea26ea693a72f7304edbd9952 Mon Sep 17 00:00:00 2001 From: willjsw Date: Mon, 16 Jun 2025 13:05:05 +0900 Subject: [PATCH 06/20] =?UTF-8?q?KW-659/feat:=20acapy=20errorcode=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/errorcode/AcapyErrorCode.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/main/java/com/doubleo/didagent/global/exception/errorcode/AcapyErrorCode.java diff --git a/src/main/java/com/doubleo/didagent/global/exception/errorcode/AcapyErrorCode.java b/src/main/java/com/doubleo/didagent/global/exception/errorcode/AcapyErrorCode.java new file mode 100644 index 0000000..c7cd1e7 --- /dev/null +++ b/src/main/java/com/doubleo/didagent/global/exception/errorcode/AcapyErrorCode.java @@ -0,0 +1,23 @@ +package com.doubleo.didagent.global.exception.errorcode; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum AcapyErrorCode implements BaseErrorCode { + INVITATION_NOT_FOUND(HttpStatus.NOT_FOUND, "invitation 을 찾을 수 없습니다."), + MEMBER_CONNECTION_NOT_FOUND(HttpStatus.NOT_FOUND, "member connection 을 찾을 수 없습니다."), + INVITATION_REQUEST_FAILED(HttpStatus.BAD_REQUEST, "invitation 생성 요청에 실패샜습니다"), + DID_PROCESS_FAILED(HttpStatus.BAD_REQUEST, "DID 생성 요청에 실패샜습니다"), + ; + + private final HttpStatus httpStatus; + private final String message; + + @Override + public String errorClassName() { + return this.name(); + } +} From a96abbeb37351b61e20eb4acf73a75ce96844f3d Mon Sep 17 00:00:00 2001 From: willjsw Date: Mon, 16 Jun 2025 13:08:20 +0900 Subject: [PATCH 07/20] =?UTF-8?q?KW-659/feat:=20hospital=20invitation=20re?= =?UTF-8?q?q/res,=20mediator=20res=20dto=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/poll/HospitalInvitationInfoRequest.java | 5 +++++ .../didagent/dto/response/poll/InvitationInfoResponse.java | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 src/main/java/com/doubleo/didagent/dto/request/poll/HospitalInvitationInfoRequest.java create mode 100644 src/main/java/com/doubleo/didagent/dto/response/poll/InvitationInfoResponse.java diff --git a/src/main/java/com/doubleo/didagent/dto/request/poll/HospitalInvitationInfoRequest.java b/src/main/java/com/doubleo/didagent/dto/request/poll/HospitalInvitationInfoRequest.java new file mode 100644 index 0000000..b4339ea --- /dev/null +++ b/src/main/java/com/doubleo/didagent/dto/request/poll/HospitalInvitationInfoRequest.java @@ -0,0 +1,5 @@ +package com.doubleo.didagent.dto.request.poll; + +import jakarta.validation.constraints.NotNull; + +public record HospitalInvitationInfoRequest(@NotNull Long passId, @NotNull Long hospitalId) {} diff --git a/src/main/java/com/doubleo/didagent/dto/response/poll/InvitationInfoResponse.java b/src/main/java/com/doubleo/didagent/dto/response/poll/InvitationInfoResponse.java new file mode 100644 index 0000000..91278ce --- /dev/null +++ b/src/main/java/com/doubleo/didagent/dto/response/poll/InvitationInfoResponse.java @@ -0,0 +1,5 @@ +package com.doubleo.didagent.dto.response.poll; + +import jakarta.validation.constraints.NotBlank; + +public record InvitationInfoResponse(@NotBlank String invitationUrl) {} From 28436de5ef545ed3eaa16421e7b781259d041fa3 Mon Sep 17 00:00:00 2001 From: willjsw Date: Mon, 16 Jun 2025 13:13:52 +0900 Subject: [PATCH 08/20] =?UTF-8?q?KW-659/feat:=20MemberConnection=20redis?= =?UTF-8?q?=20=EB=8F=84=EB=A9=94=EC=9D=B8=20=EC=84=A4=EA=B3=84=20=EB=B0=8F?= =?UTF-8?q?=20MVC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/MemberPollController.java | 28 +++++++++ .../domain/domain/MemberConnection.java | 61 +++++++++++++++++++ .../MemberConnectionRepository.java | 9 +++ .../didagent/service/MemberPollService.java | 58 ++++++++++++++++++ 4 files changed, 156 insertions(+) create mode 100644 src/main/java/com/doubleo/didagent/controller/MemberPollController.java create mode 100644 src/main/java/com/doubleo/didagent/domain/domain/MemberConnection.java create mode 100644 src/main/java/com/doubleo/didagent/domain/repository/MemberConnectionRepository.java create mode 100644 src/main/java/com/doubleo/didagent/service/MemberPollService.java diff --git a/src/main/java/com/doubleo/didagent/controller/MemberPollController.java b/src/main/java/com/doubleo/didagent/controller/MemberPollController.java new file mode 100644 index 0000000..261102d --- /dev/null +++ b/src/main/java/com/doubleo/didagent/controller/MemberPollController.java @@ -0,0 +1,28 @@ +package com.doubleo.didagent.controller; + +import com.doubleo.didagent.dto.request.poll.HospitalInvitationInfoRequest; +import com.doubleo.didagent.dto.response.poll.InvitationInfoResponse; +import com.doubleo.didagent.service.MemberPollService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/polls") +@RequiredArgsConstructor +@Slf4j +public class MemberPollController { + + private final MemberPollService memberPollService; + + @GetMapping("/mediator-invitation") + public InvitationInfoResponse getMediatorInvitationInfo() { + return memberPollService.getMediatorInvitation(); + } + + @PostMapping("/hospital-invitation") + public InvitationInfoResponse getHospitalInvitationInfo( + @RequestBody HospitalInvitationInfoRequest request) { + return memberPollService.getHospitalInvitation(request); + } +} diff --git a/src/main/java/com/doubleo/didagent/domain/domain/MemberConnection.java b/src/main/java/com/doubleo/didagent/domain/domain/MemberConnection.java new file mode 100644 index 0000000..c0463a1 --- /dev/null +++ b/src/main/java/com/doubleo/didagent/domain/domain/MemberConnection.java @@ -0,0 +1,61 @@ +package com.doubleo.didagent.domain.domain; + +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import org.springframework.data.annotation.Id; +import org.springframework.data.redis.core.RedisHash; +import org.springframework.data.redis.core.TimeToLive; +import org.springframework.data.redis.core.index.Indexed; + +@Getter +@RedisHash("connection") +public class MemberConnection { + + @Id private final String connectionId; + private final String tenantId; + @Indexed private final String passId; + private final String memberId; + private final ConnectionStatus connectionStatus; + + @TimeToLive private final long ttl; + + @Builder(access = AccessLevel.PRIVATE) + private MemberConnection( + String tenantId, + String passId, + String memberId, + String connectionId, + ConnectionStatus connectionStatus, + long ttl) { + this.connectionId = connectionId; + this.tenantId = tenantId; + this.passId = passId; + this.memberId = memberId; + this.connectionStatus = connectionStatus; + this.ttl = ttl; + } + + public static MemberConnection createMemberConnection( + String connectionId, String tenantId, String passId, String memberId) { + return builder() + .connectionId(connectionId) + .tenantId(tenantId) + .passId(passId) + .memberId(memberId) + .connectionStatus(ConnectionStatus.ACTIVE) + .ttl(1000L * 3600 * 24 * 3) + .build(); + } + + public MemberConnection updateConnectionStatus(ConnectionStatus newStatus) { + return builder() + .connectionId(this.connectionId) + .tenantId(this.tenantId) + .passId(this.passId) + .memberId(this.memberId) + .connectionStatus(newStatus) + .ttl(this.ttl) + .build(); + } +} diff --git a/src/main/java/com/doubleo/didagent/domain/repository/MemberConnectionRepository.java b/src/main/java/com/doubleo/didagent/domain/repository/MemberConnectionRepository.java new file mode 100644 index 0000000..ac5957e --- /dev/null +++ b/src/main/java/com/doubleo/didagent/domain/repository/MemberConnectionRepository.java @@ -0,0 +1,9 @@ +package com.doubleo.didagent.domain.repository; + +import com.doubleo.didagent.domain.domain.MemberConnection; +import java.util.Optional; +import org.springframework.data.repository.CrudRepository; + +public interface MemberConnectionRepository extends CrudRepository { + Optional findMemberConnectionByConnectionId(String connectionId); +} diff --git a/src/main/java/com/doubleo/didagent/service/MemberPollService.java b/src/main/java/com/doubleo/didagent/service/MemberPollService.java new file mode 100644 index 0000000..2f2482f --- /dev/null +++ b/src/main/java/com/doubleo/didagent/service/MemberPollService.java @@ -0,0 +1,58 @@ +package com.doubleo.didagent.service; + +import com.doubleo.didagent.agent.MediatorAgent; +import com.doubleo.didagent.domain.repository.HospitalInvitationRepository; +import com.doubleo.didagent.dto.request.mediator.MediatorInvitationCreateRequest; +import com.doubleo.didagent.dto.request.poll.HospitalInvitationInfoRequest; +import com.doubleo.didagent.dto.response.poll.InvitationInfoResponse; +import com.doubleo.didagent.global.exception.CommonException; +import com.doubleo.didagent.global.exception.errorcode.AcapyErrorCode; +import com.doubleo.didagent.grpc.client.HospitalTenantClient; +import java.time.Duration; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Transactional +@RequiredArgsConstructor +@Slf4j +public class MemberPollService { + + private String tenantId = "test-tenant-1"; + + private final MediatorAgent mediatorAgent; + private final HospitalInvitationRepository hospitalInvitationRepository; + private final HospitalTenantClient hospitalTenantClient; + + @Transactional(readOnly = true) + public InvitationInfoResponse getMediatorInvitation() { + return new InvitationInfoResponse( + mediatorAgent + .createMediatorInvitation(MediatorInvitationCreateRequest.generate()) + .block(Duration.ofMillis(10000)) + .invitationUrl()); + } + + @Transactional(readOnly = true) + public InvitationInfoResponse getHospitalInvitation(HospitalInvitationInfoRequest request) { + System.out.println(request.hospitalId()); + System.out.println(request.passId()); + // String tenantId = + // hospitalTenantClient.getTenantIdByHospitalId(request.hospitalId()); + return new InvitationInfoResponse(getHospitalInvitationUrl(request.passId(), tenantId)); + } + + private String getHospitalInvitationUrl(Long passId, String tenantId) { + System.out.println( + hospitalInvitationRepository + .findHospitalInvitationByPassIdAndTenantId(String.valueOf(passId), tenantId) + .orElseThrow( + () -> new CommonException(AcapyErrorCode.INVITATION_NOT_FOUND))); + return hospitalInvitationRepository + .findHospitalInvitationByPassIdAndTenantId(String.valueOf(passId), tenantId) + .orElseThrow(() -> new CommonException(AcapyErrorCode.INVITATION_NOT_FOUND)) + .getInvitationUrl(); + } +} From fd56b30054a54e96c7d32e4581130af93f907471 Mon Sep 17 00:00:00 2001 From: willjsw Date: Mon, 16 Jun 2025 13:15:15 +0900 Subject: [PATCH 09/20] =?UTF-8?q?KW-659/feat:=20connectionStatus=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/domain/ConnectionStatus.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/main/java/com/doubleo/didagent/domain/domain/ConnectionStatus.java diff --git a/src/main/java/com/doubleo/didagent/domain/domain/ConnectionStatus.java b/src/main/java/com/doubleo/didagent/domain/domain/ConnectionStatus.java new file mode 100644 index 0000000..9095e14 --- /dev/null +++ b/src/main/java/com/doubleo/didagent/domain/domain/ConnectionStatus.java @@ -0,0 +1,28 @@ +package com.doubleo.didagent.domain.domain; + +public enum ConnectionStatus { + VC_OFFERED("vc_offered"), + VC_ISSUED("vc_issued"), + ACTIVE("active"), + INACTIVE("inactive"), + ERROR("error"); + + private final String value; + + ConnectionStatus(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public static ConnectionStatus fromValue(String value) { + for (ConnectionStatus status : ConnectionStatus.values()) { + if (status.value.equalsIgnoreCase(value)) { + return status; + } + } + throw new IllegalArgumentException("Unknown ConnectionStatus: " + value); + } +} From e1873da4bcdc1c03c4e936ffd36429603de44141 Mon Sep 17 00:00:00 2001 From: willjsw Date: Mon, 16 Jun 2025 13:20:28 +0900 Subject: [PATCH 10/20] =?UTF-8?q?KW-659/feat:=20HospitalInvitation=20redis?= =?UTF-8?q?=20=EB=8F=84=EB=A9=94=EC=9D=B8=20=EA=B5=AC=ED=98=84=20=EB=B0=8F?= =?UTF-8?q?=20repository=20=EA=B5=AC=ED=98=84,=20acapyClient=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../didagent/agent/client/AcapyClient.java | 13 +++++ .../agent/client/AcapyClientConfig.java | 27 ++++++++++ .../domain/domain/HospitalInvitation.java | 54 +++++++++++++++++++ .../HospitalInvitationRepository.java | 15 ++++++ 4 files changed, 109 insertions(+) create mode 100644 src/main/java/com/doubleo/didagent/agent/client/AcapyClient.java create mode 100644 src/main/java/com/doubleo/didagent/agent/client/AcapyClientConfig.java create mode 100644 src/main/java/com/doubleo/didagent/domain/domain/HospitalInvitation.java create mode 100644 src/main/java/com/doubleo/didagent/domain/repository/HospitalInvitationRepository.java diff --git a/src/main/java/com/doubleo/didagent/agent/client/AcapyClient.java b/src/main/java/com/doubleo/didagent/agent/client/AcapyClient.java new file mode 100644 index 0000000..adf4f67 --- /dev/null +++ b/src/main/java/com/doubleo/didagent/agent/client/AcapyClient.java @@ -0,0 +1,13 @@ +package com.doubleo.didagent.agent.client; + +import lombok.Getter; +import org.springframework.web.reactive.function.client.WebClient; + +@Getter +public class AcapyClient { + private final WebClient webClient; + + public AcapyClient(WebClient webClient) { + this.webClient = webClient; + } +} diff --git a/src/main/java/com/doubleo/didagent/agent/client/AcapyClientConfig.java b/src/main/java/com/doubleo/didagent/agent/client/AcapyClientConfig.java new file mode 100644 index 0000000..8bb5f36 --- /dev/null +++ b/src/main/java/com/doubleo/didagent/agent/client/AcapyClientConfig.java @@ -0,0 +1,27 @@ +package com.doubleo.didagent.agent.client; + +import com.doubleo.didagent.infra.config.hospital.HospitalProperties; +import com.doubleo.didagent.infra.config.mediator.MediatorProperties; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.reactive.function.client.WebClient; + +@Configuration +@RequiredArgsConstructor +public class AcapyClientConfig { + private final MediatorProperties mediatorProperties; + private final HospitalProperties hospitalProperties; + + @Bean("mediatorClient") + public AcapyClient mediatorClient(WebClient.Builder builder) { + WebClient wc = builder.baseUrl(mediatorProperties.adminUrl()).build(); + return new AcapyClient(wc); + } + + @Bean("hospitalClient") + public AcapyClient hospitalClient(WebClient.Builder builder) { + WebClient wc = builder.baseUrl(hospitalProperties.adminUrl()).build(); + return new AcapyClient(wc); + } +} diff --git a/src/main/java/com/doubleo/didagent/domain/domain/HospitalInvitation.java b/src/main/java/com/doubleo/didagent/domain/domain/HospitalInvitation.java new file mode 100644 index 0000000..12c4c22 --- /dev/null +++ b/src/main/java/com/doubleo/didagent/domain/domain/HospitalInvitation.java @@ -0,0 +1,54 @@ +package com.doubleo.didagent.domain.domain; + +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import org.springframework.data.annotation.Id; +import org.springframework.data.redis.core.RedisHash; +import org.springframework.data.redis.core.TimeToLive; +import org.springframework.data.redis.core.index.Indexed; + +@Getter +@RedisHash("hospital-invitation") +public class HospitalInvitation { + + @Id private final String invitationId; + private final String invitationUrl; + private final String memberId; + @Indexed private final String passId; + @Indexed private final String tenantId; + + @TimeToLive private final long ttl; + + @Builder(access = AccessLevel.PRIVATE) + private HospitalInvitation( + String invitationId, + String invitationUrl, + String tenantId, + String passId, + String memberId, + long ttl) { + this.invitationId = invitationId; + this.invitationUrl = invitationUrl; + this.tenantId = tenantId; + this.passId = passId; + this.memberId = memberId; + this.ttl = ttl; + } + + public static HospitalInvitation createHospitalInvitation( + String invitationId, + String invitationUrl, + String tenantId, + String passId, + String memberId) { + return builder() + .invitationId(invitationId) + .invitationUrl(invitationUrl) + .tenantId(tenantId) + .passId(passId) + .memberId(memberId) + .ttl(1000L * 3600 * 24 * 3) + .build(); + } +} diff --git a/src/main/java/com/doubleo/didagent/domain/repository/HospitalInvitationRepository.java b/src/main/java/com/doubleo/didagent/domain/repository/HospitalInvitationRepository.java new file mode 100644 index 0000000..9070c18 --- /dev/null +++ b/src/main/java/com/doubleo/didagent/domain/repository/HospitalInvitationRepository.java @@ -0,0 +1,15 @@ +package com.doubleo.didagent.domain.repository; + +import com.doubleo.didagent.domain.domain.HospitalInvitation; +import java.util.Optional; +import org.springframework.data.repository.CrudRepository; + +public interface HospitalInvitationRepository extends CrudRepository { + + Optional findByInvitationId(String invitationId); + + Optional findHospitalInvitationByPassIdAndTenantId( + String passId, String tenantId); + + void deleteHospitalInvitationByInvitationId(String invitationId); +} From eae5f8c25a159510c5e669c36a41437180e76ae5 Mon Sep 17 00:00:00 2001 From: willjsw Date: Mon, 16 Jun 2025 13:23:13 +0900 Subject: [PATCH 11/20] =?UTF-8?q?KW-659/refactor:=20did=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EB=94=94=EB=A0=89=ED=86=A0=EB=A6=AC=20=EC=88=98=EC=A0=95/?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/doubleo/didagent/controller/DidController.java | 4 ++-- .../didagent/dto/request/did/DidCreateRequest.java | 8 ++++++++ .../didagent/dto/response/did/DidCreateResponse.java | 10 ++++++++++ .../java/com/doubleo/didagent/service/DidService.java | 4 ++-- 4 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/doubleo/didagent/dto/request/did/DidCreateRequest.java create mode 100644 src/main/java/com/doubleo/didagent/dto/response/did/DidCreateResponse.java diff --git a/src/main/java/com/doubleo/didagent/controller/DidController.java b/src/main/java/com/doubleo/didagent/controller/DidController.java index 73384f5..ea5a6d7 100644 --- a/src/main/java/com/doubleo/didagent/controller/DidController.java +++ b/src/main/java/com/doubleo/didagent/controller/DidController.java @@ -1,7 +1,7 @@ package com.doubleo.didagent.controller; -import com.doubleo.didagent.dto.request.DidCreateRequest; -import com.doubleo.didagent.dto.response.DidCreateResponse; +import com.doubleo.didagent.dto.request.did.DidCreateRequest; +import com.doubleo.didagent.dto.response.did.DidCreateResponse; import com.doubleo.didagent.service.DidService; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/doubleo/didagent/dto/request/did/DidCreateRequest.java b/src/main/java/com/doubleo/didagent/dto/request/did/DidCreateRequest.java new file mode 100644 index 0000000..491fced --- /dev/null +++ b/src/main/java/com/doubleo/didagent/dto/request/did/DidCreateRequest.java @@ -0,0 +1,8 @@ +package com.doubleo.didagent.dto.request.did; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.util.List; + +public record DidCreateRequest( + @NotNull List routingKeys, @NotBlank String serviceEndpoint) {} diff --git a/src/main/java/com/doubleo/didagent/dto/response/did/DidCreateResponse.java b/src/main/java/com/doubleo/didagent/dto/response/did/DidCreateResponse.java new file mode 100644 index 0000000..4200e81 --- /dev/null +++ b/src/main/java/com/doubleo/didagent/dto/response/did/DidCreateResponse.java @@ -0,0 +1,10 @@ +package com.doubleo.didagent.dto.response.did; + +import jakarta.validation.constraints.NotBlank; + +public record DidCreateResponse( + @NotBlank String peerDid2, + @NotBlank String signingKeyMb58, + @NotBlank String signingPrivBase58, + @NotBlank String agreementKeyMb58, + @NotBlank String x25519PrivateMb58) {} diff --git a/src/main/java/com/doubleo/didagent/service/DidService.java b/src/main/java/com/doubleo/didagent/service/DidService.java index 9f462e5..14cd517 100644 --- a/src/main/java/com/doubleo/didagent/service/DidService.java +++ b/src/main/java/com/doubleo/didagent/service/DidService.java @@ -1,7 +1,7 @@ package com.doubleo.didagent.service; -import com.doubleo.didagent.dto.request.DidCreateRequest; -import com.doubleo.didagent.dto.response.DidCreateResponse; +import com.doubleo.didagent.dto.request.did.DidCreateRequest; +import com.doubleo.didagent.dto.response.did.DidCreateResponse; import com.doubleo.didagent.global.exception.CommonException; import com.doubleo.didagent.global.exception.errorcode.DidErrorCode; import com.doubleo.didagent.global.util.Ed25519KeyGenerator; From 6557ac44bf3a7cab47af5e4d9b80e9ec81556ff4 Mon Sep 17 00:00:00 2001 From: willjsw Date: Mon, 16 Jun 2025 13:24:36 +0900 Subject: [PATCH 12/20] =?UTF-8?q?KW-659/feat:=20grpc=20interface=20?= =?UTF-8?q?=EB=AA=A8=EB=93=88=20=EC=B6=94=EA=B0=80(server:=20acapy,=20clie?= =?UTF-8?q?nt:=20tenant,=20pass)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../grpc/client/HospitalTenantClient.java | 26 ++++++-- .../didagent/grpc/client/PassClient.java | 29 +++++++++ .../grpc/server/AcapyGrpcServiceImpl.java | 60 ++++++++++++++----- 3 files changed, 96 insertions(+), 19 deletions(-) create mode 100644 src/main/java/com/doubleo/didagent/grpc/client/PassClient.java diff --git a/src/main/java/com/doubleo/didagent/grpc/client/HospitalTenantClient.java b/src/main/java/com/doubleo/didagent/grpc/client/HospitalTenantClient.java index 3ed5b65..26a3b73 100644 --- a/src/main/java/com/doubleo/didagent/grpc/client/HospitalTenantClient.java +++ b/src/main/java/com/doubleo/didagent/grpc/client/HospitalTenantClient.java @@ -1,9 +1,8 @@ package com.doubleo.didagent.grpc.client; -import com.doubleo.tenantservice.domain.tenant.grpc.HospitalTenantServiceGrpc; -import com.doubleo.tenantservice.domain.tenant.grpc.TenantWalletToken; -import com.doubleo.tenantservice.domain.tenant.grpc.UpdateTokensRequest; -import com.doubleo.tenantservice.domain.tenant.grpc.UpdateTokensResponse; +import com.doubleo.tenantservice.domain.tenant.grpc.*; +import io.grpc.Status; +import io.grpc.StatusRuntimeException; import java.util.Map; import lombok.extern.slf4j.Slf4j; import net.devh.boot.grpc.client.inject.GrpcClient; @@ -32,4 +31,23 @@ public UpdateTokensResponse updateTokens(Map tokens) { return blockingStub.updateTokensByTenantId(request); } + + public GetTokenResponse getTokenByTenantId(String tenantId) { + GetTokensRequest.Builder builder = GetTokensRequest.newBuilder(); + GetTokensRequest request = builder.setTenantId(tenantId).build(); + return blockingStub.getTokenByTenantId(request); + } + + public String getTenantIdByHospitalId(Long hospitalId) { + + try { + HospitalIdToTenantIdRequest request = + HospitalIdToTenantIdRequest.newBuilder().setHospitalId(hospitalId).build(); + HospitalIdToTenantIdResponse response = blockingStub.getTenantIdByHospitalId(request); + return response.getTenantId(); + } catch (Exception e) { + throw new StatusRuntimeException( + Status.INTERNAL.withDescription(e.getMessage()).withCause(e)); + } + } } diff --git a/src/main/java/com/doubleo/didagent/grpc/client/PassClient.java b/src/main/java/com/doubleo/didagent/grpc/client/PassClient.java new file mode 100644 index 0000000..099c491 --- /dev/null +++ b/src/main/java/com/doubleo/didagent/grpc/client/PassClient.java @@ -0,0 +1,29 @@ +package com.doubleo.didagent.grpc.client; + +import com.doubleo.passservice.grpc.server.PassServiceGrpc; +import com.doubleo.passservice.grpc.server.UpdateConnectionStatusRequest; +import com.doubleo.passservice.grpc.server.UpdateConnectionStatusResponse; +import lombok.extern.slf4j.Slf4j; +import net.devh.boot.grpc.client.inject.GrpcClient; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +public class PassClient { + + @GrpcClient("pass-service") + private PassServiceGrpc.PassServiceBlockingStub blockingStub; + + public UpdateConnectionStatusResponse updateConnectionStatus( + String tenantId, Long passId, String connectionId) { + UpdateConnectionStatusRequest.Builder builder = UpdateConnectionStatusRequest.newBuilder(); + + UpdateConnectionStatusRequest request = + builder.setTenantId(tenantId) + .setPassId(passId) + .setConnectionId(connectionId) + .build(); + + return blockingStub.updateConnectionState(request); + } +} diff --git a/src/main/java/com/doubleo/didagent/grpc/server/AcapyGrpcServiceImpl.java b/src/main/java/com/doubleo/didagent/grpc/server/AcapyGrpcServiceImpl.java index 3e04892..a061c9a 100644 --- a/src/main/java/com/doubleo/didagent/grpc/server/AcapyGrpcServiceImpl.java +++ b/src/main/java/com/doubleo/didagent/grpc/server/AcapyGrpcServiceImpl.java @@ -1,43 +1,73 @@ package com.doubleo.didagent.grpc.server; import com.doubleo.didagent.agent.HospitalAgent; +import com.doubleo.didagent.domain.domain.ConnectionStatus; +import com.doubleo.didagent.domain.domain.HospitalInvitation; +import com.doubleo.didagent.domain.domain.MemberConnection; +import com.doubleo.didagent.domain.repository.HospitalInvitationRepository; +import com.doubleo.didagent.domain.repository.MemberConnectionRepository; import com.doubleo.didagent.dto.request.hospital.HospitalInvitationCreateRequest; import com.doubleo.didagent.dto.response.hospital.HospitalInvitationCreateResponse; +import com.doubleo.didagent.global.exception.CommonException; +import com.doubleo.didagent.global.exception.errorcode.AcapyErrorCode; import com.doubleo.didagent.grpc.client.HospitalTenantClient; import io.grpc.stub.StreamObserver; import lombok.RequiredArgsConstructor; import net.devh.boot.grpc.server.service.GrpcService; +import org.springframework.transaction.annotation.Transactional; @GrpcService @RequiredArgsConstructor +@Transactional public class AcapyGrpcServiceImpl extends AcapyServiceGrpc.AcapyServiceImplBase { private final HospitalAgent hospitalAgent; + private final HospitalInvitationRepository hospitalInvitationRepository; + private final MemberConnectionRepository memberConnectionRepository; private final HospitalTenantClient hospitalTenantClient; @Override - public void getPassConnectionId( - PassConnectionIdRequest request, - StreamObserver responseObserver) { + public void issueVc(VcIssueRequest request, StreamObserver responseObserver) { HospitalInvitationCreateResponse invitation = hospitalAgent .createHospitalInvitation( HospitalInvitationCreateRequest.create(request.getTenantId()), - "token" - // ,hospitalTenantClient.getTenantId() - ) + hospitalTenantClient + .getTokenByTenantId(request.getTenantId()) + .getWalletToken()) .block(); - invitation.invitationUrl(); - hospitalAgent.getHospitalConnectionInfoByOobId(); - - hospitalAgent.createHospitalDid(); - hospitalAgent.postHospitalDid(); - hospitalAgent.issueHospitalVc(); - - PassConnectionIdResponse response = - PassConnectionIdResponse.newBuilder().setPassConnectionId(sb.toString()).build(); + hospitalInvitationRepository.save( + HospitalInvitation.createHospitalInvitation( + invitation.inviMsgId(), + invitation.invitationUrl(), + request.getTenantId(), + String.valueOf(request.getPassId()), + String.valueOf(request.getMemberId()))); + VcIssueResponse response = + VcIssueResponse.newBuilder().setIsInvitationCreated(true).build(); responseObserver.onNext(response); responseObserver.onCompleted(); } + + @Override + public void verifyCredential( + VerifyCredentialRequest request, + StreamObserver responseObserver) { + MemberConnection connection = + memberConnectionRepository + .findMemberConnectionByConnectionId(request.getConnectionId()) + .orElseThrow( + () -> + new CommonException( + AcapyErrorCode.MEMBER_CONNECTION_NOT_FOUND)); + if (connection.getConnectionStatus() == ConnectionStatus.VC_ISSUED) { + responseObserver.onNext( + VerifyCredentialResponse.newBuilder().setIsVerified(true).build()); + } else { + responseObserver.onNext( + VerifyCredentialResponse.newBuilder().setIsVerified(false).build()); + } + responseObserver.onCompleted(); + } } From 8e074c37a01a8e98408f1fa3b40a7adb55c0cbe1 Mon Sep 17 00:00:00 2001 From: willjsw Date: Mon, 16 Jun 2025 13:40:02 +0900 Subject: [PATCH 13/20] =?UTF-8?q?KW-659/refactor:=20test=EC=9A=A9=20?= =?UTF-8?q?=ED=95=98=EB=93=9C=EC=BD=94=EB=94=A9=EB=90=9C=20tenantId=20->?= =?UTF-8?q?=20tenantClient=EC=97=90=EC=84=9C=20=EC=A7=81=EC=A0=91=20?= =?UTF-8?q?=ED=98=B8=EC=B6=9C=20=EB=B0=8F=20=EB=B3=80=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/doubleo/didagent/service/MemberPollService.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/com/doubleo/didagent/service/MemberPollService.java b/src/main/java/com/doubleo/didagent/service/MemberPollService.java index 2f2482f..94476c7 100644 --- a/src/main/java/com/doubleo/didagent/service/MemberPollService.java +++ b/src/main/java/com/doubleo/didagent/service/MemberPollService.java @@ -20,8 +20,6 @@ @Slf4j public class MemberPollService { - private String tenantId = "test-tenant-1"; - private final MediatorAgent mediatorAgent; private final HospitalInvitationRepository hospitalInvitationRepository; private final HospitalTenantClient hospitalTenantClient; @@ -39,8 +37,7 @@ public InvitationInfoResponse getMediatorInvitation() { public InvitationInfoResponse getHospitalInvitation(HospitalInvitationInfoRequest request) { System.out.println(request.hospitalId()); System.out.println(request.passId()); - // String tenantId = - // hospitalTenantClient.getTenantIdByHospitalId(request.hospitalId()); + String tenantId = hospitalTenantClient.getTenantIdByHospitalId(request.hospitalId()); return new InvitationInfoResponse(getHospitalInvitationUrl(request.passId(), tenantId)); } From 262994c7c15fa48ab3c9f686829ad6779f745307 Mon Sep 17 00:00:00 2001 From: willjsw Date: Mon, 16 Jun 2025 13:51:59 +0900 Subject: [PATCH 14/20] =?UTF-8?q?KW-659/refactor:=20config=20&=20build.gra?= =?UTF-8?q?dle=20=EB=B0=B0=ED=8F=AC=20=ED=99=98=EA=B2=BD=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 4 ++-- src/main/resources/application.yml | 36 +++++++++--------------------- 2 files changed, 13 insertions(+), 27 deletions(-) diff --git a/build.gradle b/build.gradle index be45cf3..31d7aa5 100644 --- a/build.gradle +++ b/build.gradle @@ -42,8 +42,8 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-webflux' // //config -// implementation 'org.springframework.cloud:spring-cloud-starter-config' -// implementation 'org.springframework.cloud:spring-cloud-starter-bus-amqp' + implementation 'org.springframework.cloud:spring-cloud-starter-config' + implementation 'org.springframework.cloud:spring-cloud-starter-bus-amqp' //Swagger implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.0' diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 493bdd3..d1887ee 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,35 +1,21 @@ spring: application: name: did-agent - data: - redis: - host: localhost - port: 6379 - password: task: execution: pool: core-size: 10 max-size: 20 queue-capacity: 100 -server: - port: 8080 -management: - endpoints: - web: - exposure: - include: refresh, health, beans, httpexchanges, busrefresh, info, metrics, prometheus + config: + import: optional:configserver:${CONFIG_SERVER_URL} -# -# config: -# import: optional:configserver:${CONFIG_SERVER_URL} -# -# rabbitmq: -# host: ${RABBITMQ_HOST:localhost} -# port: ${RABBITMQ_PORT:5672} -# username: ${RABBITMQ_USERNAME:guest} -# password: ${RABBITMQ_PASSWORD:guest} -# -# profiles: -# active: ${SPRING_PROFILES_ACTIVE:local} -# include: ${SPRING_PROFILES_INCLUDE:swagger,datasource,actuator} + rabbitmq: + host: ${RABBITMQ_HOST:localhost} + port: ${RABBITMQ_PORT:5672} + username: ${RABBITMQ_USERNAME:guest} + password: ${RABBITMQ_PASSWORD:guest} + + profiles: + active: ${SPRING_PROFILES_ACTIVE:local} + include: ${SPRING_PROFILES_INCLUDE:swagger,datasource,actuator,grpc,redis} From 332f7157feb015b1f56a7e0b1e8dbbadea23509c Mon Sep 17 00:00:00 2001 From: willjsw Date: Mon, 16 Jun 2025 14:41:11 +0900 Subject: [PATCH 15/20] =?UTF-8?q?KW-659/refactor:=EB=AF=B8=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=20datasource=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index d1887ee..64c9568 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -18,4 +18,4 @@ spring: profiles: active: ${SPRING_PROFILES_ACTIVE:local} - include: ${SPRING_PROFILES_INCLUDE:swagger,datasource,actuator,grpc,redis} + include: ${SPRING_PROFILES_INCLUDE:swagger,actuator,grpc,redis} From cf1436d1e33c6eaded53324bbdde86185f47e4e7 Mon Sep 17 00:00:00 2001 From: willjsw Date: Mon, 16 Jun 2025 14:41:41 +0900 Subject: [PATCH 16/20] =?UTF-8?q?KW-659/fix:=20invitation/did=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EC=98=A4=EB=A5=98=20400=20->=20500=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../didagent/global/exception/errorcode/AcapyErrorCode.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/doubleo/didagent/global/exception/errorcode/AcapyErrorCode.java b/src/main/java/com/doubleo/didagent/global/exception/errorcode/AcapyErrorCode.java index c7cd1e7..8b786da 100644 --- a/src/main/java/com/doubleo/didagent/global/exception/errorcode/AcapyErrorCode.java +++ b/src/main/java/com/doubleo/didagent/global/exception/errorcode/AcapyErrorCode.java @@ -9,8 +9,8 @@ public enum AcapyErrorCode implements BaseErrorCode { INVITATION_NOT_FOUND(HttpStatus.NOT_FOUND, "invitation 을 찾을 수 없습니다."), MEMBER_CONNECTION_NOT_FOUND(HttpStatus.NOT_FOUND, "member connection 을 찾을 수 없습니다."), - INVITATION_REQUEST_FAILED(HttpStatus.BAD_REQUEST, "invitation 생성 요청에 실패샜습니다"), - DID_PROCESS_FAILED(HttpStatus.BAD_REQUEST, "DID 생성 요청에 실패샜습니다"), + INVITATION_REQUEST_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "invitation 생성 요청에 실패샜습니다"), + DID_PROCESS_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "DID 생성 요청에 실패샜습니다"), ; private final HttpStatus httpStatus; From a0f711060da5cb53755bfae1e70d9555fde824e2 Mon Sep 17 00:00:00 2001 From: willjsw Date: Mon, 16 Jun 2025 14:42:29 +0900 Subject: [PATCH 17/20] =?UTF-8?q?KW-659/fix:=20=EC=98=A4=ED=83=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../didagent/global/exception/errorcode/AcapyErrorCode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/doubleo/didagent/global/exception/errorcode/AcapyErrorCode.java b/src/main/java/com/doubleo/didagent/global/exception/errorcode/AcapyErrorCode.java index 8b786da..2b07a7e 100644 --- a/src/main/java/com/doubleo/didagent/global/exception/errorcode/AcapyErrorCode.java +++ b/src/main/java/com/doubleo/didagent/global/exception/errorcode/AcapyErrorCode.java @@ -10,7 +10,7 @@ public enum AcapyErrorCode implements BaseErrorCode { INVITATION_NOT_FOUND(HttpStatus.NOT_FOUND, "invitation 을 찾을 수 없습니다."), MEMBER_CONNECTION_NOT_FOUND(HttpStatus.NOT_FOUND, "member connection 을 찾을 수 없습니다."), INVITATION_REQUEST_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "invitation 생성 요청에 실패샜습니다"), - DID_PROCESS_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "DID 생성 요청에 실패샜습니다"), + DID_PROCESS_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "DID 생성 요청에 실패했습니다"), ; private final HttpStatus httpStatus; From f16e35aee54caea6651af3e4f0bbb3f82ba7d15c Mon Sep 17 00:00:00 2001 From: willjsw Date: Mon, 16 Jun 2025 14:53:29 +0900 Subject: [PATCH 18/20] =?UTF-8?q?KW-659/refactor:=20gRPC=20Clients=20try-c?= =?UTF-8?q?atch=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../grpc/client/HospitalTenantClient.java | 44 +++++++++++-------- .../didagent/grpc/client/PassClient.java | 23 +++++++--- 2 files changed, 42 insertions(+), 25 deletions(-) diff --git a/src/main/java/com/doubleo/didagent/grpc/client/HospitalTenantClient.java b/src/main/java/com/doubleo/didagent/grpc/client/HospitalTenantClient.java index 26a3b73..74bac69 100644 --- a/src/main/java/com/doubleo/didagent/grpc/client/HospitalTenantClient.java +++ b/src/main/java/com/doubleo/didagent/grpc/client/HospitalTenantClient.java @@ -17,33 +17,41 @@ public class HospitalTenantClient { public UpdateTokensResponse updateTokens(Map tokens) { UpdateTokensRequest.Builder builder = UpdateTokensRequest.newBuilder(); - - for (Map.Entry entry : tokens.entrySet()) { - TenantWalletToken token = - TenantWalletToken.newBuilder() - .setTenantId(entry.getKey()) - .setWalletToken(entry.getValue()) - .build(); - builder.addTokens(token); + try { + for (Map.Entry entry : tokens.entrySet()) { + TenantWalletToken token = + TenantWalletToken.newBuilder() + .setTenantId(entry.getKey()) + .setWalletToken(entry.getValue()) + .build(); + builder.addTokens(token); + } + return blockingStub.updateTokensByTenantId(builder.build()); + } catch (StatusRuntimeException e) { + throw new StatusRuntimeException( + Status.INTERNAL.withDescription(e.getMessage()).withCause(e)); } - - UpdateTokensRequest request = builder.build(); - - return blockingStub.updateTokensByTenantId(request); } public GetTokenResponse getTokenByTenantId(String tenantId) { - GetTokensRequest.Builder builder = GetTokensRequest.newBuilder(); - GetTokensRequest request = builder.setTenantId(tenantId).build(); - return blockingStub.getTokenByTenantId(request); + try { + return blockingStub.getTokenByTenantId( + GetTokensRequest.newBuilder().setTenantId(tenantId).build()); + + } catch (StatusRuntimeException e) { + throw new StatusRuntimeException( + Status.INTERNAL.withDescription(e.getMessage()).withCause(e)); + } } public String getTenantIdByHospitalId(Long hospitalId) { try { - HospitalIdToTenantIdRequest request = - HospitalIdToTenantIdRequest.newBuilder().setHospitalId(hospitalId).build(); - HospitalIdToTenantIdResponse response = blockingStub.getTenantIdByHospitalId(request); + HospitalIdToTenantIdResponse response = + blockingStub.getTenantIdByHospitalId( + HospitalIdToTenantIdRequest.newBuilder() + .setHospitalId(hospitalId) + .build()); return response.getTenantId(); } catch (Exception e) { throw new StatusRuntimeException( diff --git a/src/main/java/com/doubleo/didagent/grpc/client/PassClient.java b/src/main/java/com/doubleo/didagent/grpc/client/PassClient.java index 099c491..25972a5 100644 --- a/src/main/java/com/doubleo/didagent/grpc/client/PassClient.java +++ b/src/main/java/com/doubleo/didagent/grpc/client/PassClient.java @@ -3,6 +3,8 @@ import com.doubleo.passservice.grpc.server.PassServiceGrpc; import com.doubleo.passservice.grpc.server.UpdateConnectionStatusRequest; import com.doubleo.passservice.grpc.server.UpdateConnectionStatusResponse; +import io.grpc.Status; +import io.grpc.StatusRuntimeException; import lombok.extern.slf4j.Slf4j; import net.devh.boot.grpc.client.inject.GrpcClient; import org.springframework.stereotype.Component; @@ -16,14 +18,21 @@ public class PassClient { public UpdateConnectionStatusResponse updateConnectionStatus( String tenantId, Long passId, String connectionId) { - UpdateConnectionStatusRequest.Builder builder = UpdateConnectionStatusRequest.newBuilder(); - UpdateConnectionStatusRequest request = - builder.setTenantId(tenantId) - .setPassId(passId) - .setConnectionId(connectionId) - .build(); + try { + UpdateConnectionStatusRequest.Builder builder = + UpdateConnectionStatusRequest.newBuilder(); + UpdateConnectionStatusRequest request = + builder.setTenantId(tenantId) + .setPassId(passId) + .setConnectionId(connectionId) + .build(); + return blockingStub.updateConnectionState(request); - return blockingStub.updateConnectionState(request); + } catch (StatusRuntimeException e) { + log.error("Pass Connection Status 업데이트 실패: {}", e.getMessage()); + throw new StatusRuntimeException( + Status.INTERNAL.withDescription(e.getMessage()).withCause(e)); + } } } From 6e9ecc4087dd65b2a8123ead33c49c9cbef599c5 Mon Sep 17 00:00:00 2001 From: willjsw Date: Mon, 16 Jun 2025 14:56:58 +0900 Subject: [PATCH 19/20] =?UTF-8?q?KW-659/refactor:=20memberPollController?= =?UTF-8?q?=20convention=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/doubleo/didagent/controller/MemberPollController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/doubleo/didagent/controller/MemberPollController.java b/src/main/java/com/doubleo/didagent/controller/MemberPollController.java index 261102d..b1bf20a 100644 --- a/src/main/java/com/doubleo/didagent/controller/MemberPollController.java +++ b/src/main/java/com/doubleo/didagent/controller/MemberPollController.java @@ -16,12 +16,12 @@ public class MemberPollController { private final MemberPollService memberPollService; @GetMapping("/mediator-invitation") - public InvitationInfoResponse getMediatorInvitationInfo() { + public InvitationInfoResponse mediatorInvitationInfoGet() { return memberPollService.getMediatorInvitation(); } @PostMapping("/hospital-invitation") - public InvitationInfoResponse getHospitalInvitationInfo( + public InvitationInfoResponse hospitalInvitationInfoGet( @RequestBody HospitalInvitationInfoRequest request) { return memberPollService.getHospitalInvitation(request); } From 41bb1b9172878d3f97b4aad19e8cb393cb80fb25 Mon Sep 17 00:00:00 2001 From: willjsw Date: Mon, 16 Jun 2025 14:58:59 +0900 Subject: [PATCH 20/20] =?UTF-8?q?KW-659/fix:=20INVITATION=5FREQUEST=5FFAIL?= =?UTF-8?q?ED=20=EC=97=90=EB=9F=AC=20=EB=A9=94=EC=84=B8=EC=A7=80=20?= =?UTF-8?q?=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../didagent/global/exception/errorcode/AcapyErrorCode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/doubleo/didagent/global/exception/errorcode/AcapyErrorCode.java b/src/main/java/com/doubleo/didagent/global/exception/errorcode/AcapyErrorCode.java index 2b07a7e..4ad9e77 100644 --- a/src/main/java/com/doubleo/didagent/global/exception/errorcode/AcapyErrorCode.java +++ b/src/main/java/com/doubleo/didagent/global/exception/errorcode/AcapyErrorCode.java @@ -9,7 +9,7 @@ public enum AcapyErrorCode implements BaseErrorCode { INVITATION_NOT_FOUND(HttpStatus.NOT_FOUND, "invitation 을 찾을 수 없습니다."), MEMBER_CONNECTION_NOT_FOUND(HttpStatus.NOT_FOUND, "member connection 을 찾을 수 없습니다."), - INVITATION_REQUEST_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "invitation 생성 요청에 실패샜습니다"), + INVITATION_REQUEST_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "invitation 생성 요청에 실패했습니다"), DID_PROCESS_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "DID 생성 요청에 실패했습니다"), ;