Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
e535c3d
KW-659/feat: Mediator, Hospital WebClient ๊ตฌํ˜„
willjsw Jun 12, 2025
82a623e
KW-659/feat: redis ์˜์กด์„ฑ ์ถ”๊ฐ€
willjsw Jun 12, 2025
f5df495
KW-659/feat: grpc pass-interface & acapy-interface v0.3.3 ์ ์šฉ
willjsw Jun 16, 2025
4a6c4af
KW-659/feat: webflux yml config ์ถ”๊ฐ€
willjsw Jun 16, 2025
0c484bf
KW-659/feat: redis property ์ถ”๊ฐ€
willjsw Jun 16, 2025
05fd3ff
KW-659/feat: acapy errorcode ์ถ”๊ฐ€
willjsw Jun 16, 2025
a96abbe
KW-659/feat: hospital invitation req/res, mediator res dto ๊ตฌํ˜„
willjsw Jun 16, 2025
28436de
KW-659/feat: MemberConnection redis ๋„๋ฉ”์ธ ์„ค๊ณ„ ๋ฐ MVC ๊ตฌํ˜„
willjsw Jun 16, 2025
fd56b30
KW-659/feat: connectionStatus ๊ตฌํ˜„
willjsw Jun 16, 2025
e1873da
KW-659/feat: HospitalInvitation redis ๋„๋ฉ”์ธ ๊ตฌํ˜„ ๋ฐ repository ๊ตฌํ˜„, acapyClโ€ฆ
willjsw Jun 16, 2025
eae5f8c
KW-659/refactor: did ์ƒ์„ฑ ์„œ๋น„์Šค ๋กœ์ง ๋””๋ ‰ํ† ๋ฆฌ ์ˆ˜์ •/์˜์กด์„ฑ ์ˆ˜์ •
willjsw Jun 16, 2025
6557ac4
KW-659/feat: grpc interface ๋ชจ๋“ˆ ์ถ”๊ฐ€(server: acapy, client: tenant, pass)
willjsw Jun 16, 2025
8e074c3
KW-659/refactor: test์šฉ ํ•˜๋“œ์ฝ”๋”ฉ๋œ tenantId -> tenantClient์—์„œ ์ง์ ‘ ํ˜ธ์ถœ ๋ฐ ๋ณ€ํ™˜
willjsw Jun 16, 2025
262994c
KW-659/refactor: config & build.gradle ๋ฐฐํฌ ํ™˜๊ฒฝ ์„ค์ •์œผ๋กœ ๋ณ€๊ฒฝ
willjsw Jun 16, 2025
332f715
KW-659/refactor:๋ฏธ์‚ฌ์šฉ datasource ํ”„๋กœํ•„ ์ œ๊ฑฐ
willjsw Jun 16, 2025
cf1436d
KW-659/fix: invitation/did ์ƒ์„ฑ ์˜ค๋ฅ˜ 400 -> 500 ์—๋Ÿฌ๋กœ ์ˆ˜์ •
willjsw Jun 16, 2025
a0f7110
KW-659/fix: ์˜คํƒ€ ์ˆ˜์ •
willjsw Jun 16, 2025
f16e35a
KW-659/refactor: gRPC Clients try-catch ์ฒ˜๋ฆฌ
willjsw Jun 16, 2025
6e9ecc4
KW-659/refactor: memberPollController convention ์ˆ˜์ •
willjsw Jun 16, 2025
41bb1b9
KW-659/fix: INVITATION_REQUEST_FAILED ์—๋Ÿฌ ๋ฉ”์„ธ์ง€ ์˜คํƒ€ ์ˆ˜์ •
willjsw Jun 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,19 @@ 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'
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'

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'
Expand All @@ -60,7 +63,9 @@ dependencies {
implementation 'org.json:json:20240303'

// grpc interface
implementation "com.doubleo.grpc:tenant-interface:0.2.5"
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"
Expand All @@ -71,6 +76,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'
Expand Down
3 changes: 0 additions & 3 deletions src/main/java/com/doubleo/didagent/agent/AcapyAgent.java

This file was deleted.

13 changes: 13 additions & 0 deletions src/main/java/com/doubleo/didagent/agent/client/AcapyClient.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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<String, Object> payload,
@RequestHeader Map<String, String> 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);
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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 mediatorInvitationInfoGet() {
return memberPollService.getMediatorInvitation();
}

@PostMapping("/hospital-invitation")
public InvitationInfoResponse hospitalInvitationInfoGet(
@RequestBody HospitalInvitationInfoRequest request) {
return memberPollService.getHospitalInvitation(request);
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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<HospitalInvitation, String> {

Optional<HospitalInvitation> findByInvitationId(String invitationId);

Optional<HospitalInvitation> findHospitalInvitationByPassIdAndTenantId(
String passId, String tenantId);

void deleteHospitalInvitationByInvitationId(String invitationId);
}
Original file line number Diff line number Diff line change
@@ -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<MemberConnection, String> {
Optional<MemberConnection> findMemberConnectionByConnectionId(String connectionId);
}
Original file line number Diff line number Diff line change
@@ -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<String> routingKeys, @NotBlank String serviceEndpoint) {}
Original file line number Diff line number Diff line change
@@ -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<String> handshakeProtocols,
@JsonProperty("goal_code") String goalCode,
@JsonProperty("my_label") String myLabel,
@JsonProperty("accept") List<String> accept,
@JsonProperty("use_did_method") String useDidMethod,
@JsonProperty("multi_use") boolean multiUse) {
public static HospitalInvitationCreateRequest create(String tenantId) {
List<String> handshakeProtocols = new ArrayList<>();
List<String> 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);
}
}
Original file line number Diff line number Diff line change
@@ -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) {}
Original file line number Diff line number Diff line change
@@ -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) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.doubleo.didagent.dto.response.poll;

import jakarta.validation.constraints.NotBlank;

public record InvitationInfoResponse(@NotBlank String invitationUrl) {}
Loading
Loading