Skip to content

Commit

Permalink
feat : 서비스 엔드포인트 리스트 조회 API 구현
Browse files Browse the repository at this point in the history
- 네트워크 토폴로지 로직 리팩토링 CompletableFuture를 이용해서 병렬로 처리 및 Builder 클래스를 통해 추상화하여 가독성 향상
  • Loading branch information
JiwonKKang committed Nov 1, 2024
1 parent 25a5a81 commit 0672f8a
Show file tree
Hide file tree
Showing 29 changed files with 564 additions and 262 deletions.
1 change: 1 addition & 0 deletions infra/kafka/kafka_cluster.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ services:
kafka-broker-1:
image: confluentinc/cp-kafka:latest
hostname: kafka-broker-1
restart: always
depends_on:
- zookeeper
ports:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,17 @@
import com.univ.tracedin.api.project.dto.CreateProjectRequest;
import com.univ.tracedin.api.project.dto.NodeResponse;
import com.univ.tracedin.api.project.dto.ProjectResponse;
import com.univ.tracedin.api.project.dto.ServiceSearchRequest;
import com.univ.tracedin.api.project.dto.TraceSearchRequest;
import com.univ.tracedin.domain.project.NetworkTopology;
import com.univ.tracedin.domain.project.EndPointUrl;
import com.univ.tracedin.domain.project.ProjectId;
import com.univ.tracedin.domain.project.ProjectKey;
import com.univ.tracedin.domain.project.ProjectMember.MemberRole;
import com.univ.tracedin.domain.project.ProjectMemberId;
import com.univ.tracedin.domain.project.ProjectService;
import com.univ.tracedin.domain.project.ProjectStatistic;
import com.univ.tracedin.domain.project.ProjectStatistic.StatisticsType;
import com.univ.tracedin.domain.span.Topology;
import com.univ.tracedin.domain.user.UserId;

@RestController
Expand All @@ -36,62 +38,81 @@ public class ProjectApi implements ProjectApiDocs {

private final ProjectService projectService;

@Override
@PostMapping
public Response<ProjectKey> createProject(
@RequestBody CreateProjectRequest request, Long userId) {
return Response.success(
projectService.create(UserId.from(userId), request.toProjectInfo()));
}

@Override
@GetMapping
public Response<List<ProjectResponse>> projectList(Long userId) {
List<ProjectResponse> responses =
final List<ProjectResponse> responses =
projectService.getProjectList(UserId.from(userId)).stream()
.map(ProjectResponse::from)
.toList();
return Response.success(responses);
}

@Override
@DeleteMapping("/{projectId}")
public Response<Void> deleteProject(@PathVariable Long projectId) {
projectService.deleteProject(ProjectId.from(projectId));
return Response.success();
}

@Override
@GetMapping("/{projectKey}/service-nodes")
public Response<List<NodeResponse>> serviceNodes(@PathVariable String projectKey) {
List<NodeResponse> responses =
final List<NodeResponse> responses =
projectService.getServiceNodeList(ProjectKey.from(projectKey)).stream()
.map(NodeResponse::from)
.toList();
return Response.success(responses);
}

@Override
@GetMapping("/service-endpoints")
public Response<List<String>> serviceEndpoints(ServiceSearchRequest request) {
final List<String> response =
projectService.getServiceEndpoints(request.toCondition()).stream()
.map(EndPointUrl::value)
.toList();
return Response.success(response);
}

@Override
@GetMapping("/{projectKey}/network-topology")
public Response<NetworkTopology> networkTopology(@PathVariable String projectKey) {
public Response<Topology> networkTopology(@PathVariable String projectKey) {
return Response.success(projectService.getNetworkTopology(ProjectKey.from(projectKey)));
}

@Override
@GetMapping("/statistics/{statisticsType}")
public Response<ProjectStatistic<?>> statistics(
@PathVariable StatisticsType statisticsType, TraceSearchRequest request) {
return Response.success(
projectService.getStatistics(request.toCondition(), statisticsType));
}

@Override
@PostMapping("/{projectId}/members")
public Response<Void> addMember(@PathVariable Long projectId, AddMemberRequest request) {
projectService.addMember(
ProjectId.from(projectId), request.targetMemberEmail(), request.role());
return Response.success();
}

@Override
@DeleteMapping("/members/{projectMemberId}")
public Response<Void> removeMember(@PathVariable Long projectMemberId) {
projectService.removeMember(ProjectMemberId.from(projectMemberId));
return Response.success();
}

@Override
@PatchMapping("/members/{projectMemberId}")
public Response<Void> changeRole(@PathVariable Long projectMemberId, MemberRole targetRole) {
projectService.changeRole(ProjectMemberId.from(projectMemberId), targetRole);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@
import com.univ.tracedin.api.project.dto.CreateProjectRequest;
import com.univ.tracedin.api.project.dto.NodeResponse;
import com.univ.tracedin.api.project.dto.ProjectResponse;
import com.univ.tracedin.api.project.dto.ServiceSearchRequest;
import com.univ.tracedin.api.project.dto.TraceSearchRequest;
import com.univ.tracedin.domain.project.NetworkTopology;
import com.univ.tracedin.domain.project.ProjectKey;
import com.univ.tracedin.domain.project.ProjectMember.MemberRole;
import com.univ.tracedin.domain.project.ProjectStatistic;
import com.univ.tracedin.domain.project.ProjectStatistic.StatisticsType;
import com.univ.tracedin.domain.span.Topology;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
Expand All @@ -34,8 +35,11 @@ public interface ProjectApiDocs {
@Operation(summary = "서비스 리스트 조회", description = "프로젝트의 서비스 노드 리스트를 조회합니다.")
Response<List<NodeResponse>> serviceNodes(String projectKey);

@Operation(summary = "서비스의 엔드포인트 리스트 조회", description = "프로젝트의 서비스의 엔드포인트 리스트를 조회합니다.")
Response<List<String>> serviceEndpoints(ServiceSearchRequest request);

@Operation(summary = "네트워크 토폴로지 조회", description = "프로젝트의 네트워크 토폴로지를 조회합니다.")
Response<NetworkTopology> networkTopology(String projectKey);
Response<Topology> networkTopology(String projectKey);

@Operation(
summary = "프로젝트 통계 조회",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.univ.tracedin.api.project.dto;

import com.univ.tracedin.domain.project.ProjectKey;
import com.univ.tracedin.domain.project.ServiceSearchCondition;

public record ServiceSearchRequest(String projectKey, String serviceName) {
public ServiceSearchCondition toCondition() {
return ServiceSearchCondition.builder()
.projectKey(ProjectKey.from(projectKey))
.serviceName(serviceName)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotBlank;

import com.univ.tracedin.domain.project.EndPointUrl;
import com.univ.tracedin.domain.project.ProjectKey;
import com.univ.tracedin.domain.project.TraceSearchCondition;

Expand All @@ -18,8 +19,13 @@ public record TraceSearchRequest(
LocalDateTime endTime) {

public TraceSearchCondition toCondition() {
return new TraceSearchCondition(
new ProjectKey(projectKey), serviceName, endPointUrl, startTime, endTime);
return TraceSearchCondition.builder()
.projectKey(ProjectKey.from(projectKey))
.serviceName(serviceName)
.endPointUrl(EndPointUrl.from(endPointUrl))
.startTime(startTime)
.endTime(endTime)
.build();
}

// 종료 시간만 있을 수 없도록 검증
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
@RequiredArgsConstructor
public class ServiceMetricsService {

private final ServiceMetricsReader serviceMetricReader;
private final ServiceMetricsMessagePublisher serviceMetricsMessagePublisher;

public void appendMetrics(ServiceMetrics metrics) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.univ.tracedin.domain.project;

import java.util.regex.Pattern;

public record EndPointUrl(String value) {

private static final Pattern ENDPOINT_PATTERN =
Pattern.compile("^(https?://)([a-zA-Z0-9.-]+)(:[0-9]+)?(/[a-zA-Z0-9._-]+)*$");

public static EndPointUrl from(String url) {
if (url == null || !ENDPOINT_PATTERN.matcher(url).matches()) {
throw new IllegalArgumentException("Invalid endpoint URL format: " + url);
}
return new EndPointUrl(url);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,18 @@
import lombok.AllArgsConstructor;
import lombok.Getter;

import com.univ.tracedin.domain.span.Topology;

@Getter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class NetworkTopology {
@AllArgsConstructor
public final class NetworkTopology implements Topology {

private final List<Node> nodes;
private final List<Edge> edges;

public static NetworkTopology of(List<Node> nodes, List<Edge> edges) {
return new NetworkTopology(nodes, edges);
}

@Getter
@AllArgsConstructor
public static class Node {
public static final class Node {

private ProjectKey projectKey;
private String name;
Expand Down Expand Up @@ -62,7 +60,7 @@ public enum NodeType {

@Getter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public static class Edge {
public static final class Edge {

private String source;
private String target;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.univ.tracedin.domain.project;

import com.univ.tracedin.domain.span.Topology;

public interface NetworkTopologyAnalyer {

Topology analyze(Project projectKey);
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package com.univ.tracedin.domain.project;

import java.util.List;

public interface ProjectAnalyzer {

ProjectStatistic<?> analyze(
TraceSearchCondition cond, ProjectStatistic.StatisticsType statisticsType);

List<EndPointUrl> getEndpoints(ServiceSearchCondition cond);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import lombok.RequiredArgsConstructor;

import com.univ.tracedin.domain.span.Topology;
import com.univ.tracedin.domain.user.User;
import com.univ.tracedin.domain.user.UserId;
import com.univ.tracedin.domain.user.UserReader;
Expand All @@ -24,53 +25,58 @@ public class ProjectService {
private final ProjectDeleter projectDeleter;
private final ProjectValidator projectValidator;
private final ProjectMemberManager projectMemberManager;
private final NetworkTopologyBuilder networkTopologyBuilder;
private final NetworkTopologyAnalyer networkTopologyAnalyer;
private final ProjectAnalyzer projectAnalyzer;

public ProjectKey create(UserId creatorId, ProjectInfo projectInfo) {
User user = userReader.read(creatorId);
final User user = userReader.read(creatorId);
return projectAppender.append(user, projectInfo);
}

public List<Project> getProjectList(UserId userId) {
User user = userReader.read(userId);
final User user = userReader.read(userId);
return projectReader.readAll(user);
}

public List<Node> getServiceNodeList(ProjectKey projectKey) {
Project project = projectReader.readByKey(projectKey);
final Project project = projectReader.readByKey(projectKey);
return projectReader.readServiceNods(project);
}

public NetworkTopology getNetworkTopology(ProjectKey projectKey) {
Project project = projectReader.readByKey(projectKey);
return networkTopologyBuilder.build(project);
public List<EndPointUrl> getServiceEndpoints(ServiceSearchCondition cond) {
projectValidator.validate(cond.getProjectKey());
return projectAnalyzer.getEndpoints(cond);
}

public Topology getNetworkTopology(ProjectKey projectKey) {
final Project project = projectReader.readByKey(projectKey);
return networkTopologyAnalyer.analyze(project);
}

public ProjectStatistic<?> getStatistics(
TraceSearchCondition cond, StatisticsType statisticsType) {
projectValidator.validate(cond.projectKey());
projectValidator.validate(cond.getProjectKey());
return projectAnalyzer.analyze(cond, statisticsType);
}

public void addMember(ProjectId projectId, String targetMemberEmail, MemberRole role) {
Project project = projectReader.read(projectId);
User targetUser = userReader.read(targetMemberEmail);
final Project project = projectReader.read(projectId);
final User targetUser = userReader.read(targetMemberEmail);
projectMemberManager.add(project, targetUser, role);
}

public void removeMember(ProjectMemberId projectMemberId) {
ProjectMember projectMember = projectMemberManager.read(projectMemberId);
final ProjectMember projectMember = projectMemberManager.read(projectMemberId);
projectMemberManager.remove(projectMember);
}

public void changeRole(ProjectMemberId projectMemberId, MemberRole role) {
ProjectMember projectMember = projectMemberManager.read(projectMemberId);
final ProjectMember projectMember = projectMemberManager.read(projectMemberId);
projectMemberManager.changeRole(projectMember, role);
}

public void deleteProject(ProjectId projectId) {
Project project = projectReader.read(projectId);
final Project project = projectReader.read(projectId);
projectMemberManager.removeAll(project);
projectDeleter.delete(project);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.univ.tracedin.domain.project;

import lombok.Builder;
import lombok.Getter;
import lombok.experimental.SuperBuilder;

import io.micrometer.common.util.StringUtils;

@Getter
@SuperBuilder
@Builder
public class ServiceSearchCondition {
protected ProjectKey projectKey;
protected String serviceName;

protected ServiceSearchCondition(ProjectKey projectKey, String serviceName) {
this.projectKey = projectKey;
this.serviceName = serviceName;
}

protected ServiceSearchCondition() {}

public boolean hasServiceName() {
return StringUtils.isNotBlank(serviceName);
}
}
Loading

0 comments on commit 0672f8a

Please sign in to comment.