1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.linecorp.centraldogma.server.internal.api;
18
19 import static com.google.common.collect.ImmutableList.toImmutableList;
20 import static com.linecorp.centraldogma.server.internal.api.HttpApiUtil.checkStatusArgument;
21 import static com.linecorp.centraldogma.server.internal.api.HttpApiUtil.checkUnremoveArgument;
22 import static com.linecorp.centraldogma.server.internal.api.HttpApiUtil.returnOrThrow;
23 import static java.util.Objects.requireNonNull;
24
25 import java.util.List;
26 import java.util.concurrent.CompletableFuture;
27
28 import javax.annotation.Nullable;
29
30 import com.fasterxml.jackson.databind.JsonNode;
31
32 import com.linecorp.armeria.common.ContextAwareBlockingTaskExecutor;
33 import com.linecorp.armeria.server.ServiceRequestContext;
34 import com.linecorp.armeria.server.annotation.Consumes;
35 import com.linecorp.armeria.server.annotation.Delete;
36 import com.linecorp.armeria.server.annotation.Get;
37 import com.linecorp.armeria.server.annotation.Param;
38 import com.linecorp.armeria.server.annotation.Patch;
39 import com.linecorp.armeria.server.annotation.Post;
40 import com.linecorp.armeria.server.annotation.ProducesJson;
41 import com.linecorp.armeria.server.annotation.ResponseConverter;
42 import com.linecorp.armeria.server.annotation.StatusCode;
43 import com.linecorp.centraldogma.common.Author;
44 import com.linecorp.centraldogma.common.ProjectRole;
45 import com.linecorp.centraldogma.internal.api.v1.CreateProjectRequest;
46 import com.linecorp.centraldogma.internal.api.v1.ProjectDto;
47 import com.linecorp.centraldogma.server.command.CommandExecutor;
48 import com.linecorp.centraldogma.server.internal.api.auth.RequiresProjectRole;
49 import com.linecorp.centraldogma.server.internal.api.auth.RequiresSystemAdministrator;
50 import com.linecorp.centraldogma.server.internal.api.converter.CreateApiResponseConverter;
51 import com.linecorp.centraldogma.server.internal.storage.project.ProjectApiManager;
52 import com.linecorp.centraldogma.server.metadata.Member;
53 import com.linecorp.centraldogma.server.metadata.ProjectMetadata;
54 import com.linecorp.centraldogma.server.metadata.TokenRegistration;
55 import com.linecorp.centraldogma.server.metadata.User;
56 import com.linecorp.centraldogma.server.metadata.UserWithToken;
57 import com.linecorp.centraldogma.server.storage.project.Project;
58
59
60
61
62 @ProducesJson
63 public class ProjectServiceV1 extends AbstractService {
64
65 private final ProjectApiManager projectApiManager;
66
67 public ProjectServiceV1(ProjectApiManager projectApiManager, CommandExecutor executor) {
68 super(executor);
69 this.projectApiManager = requireNonNull(projectApiManager, "projectApiManager");
70 }
71
72
73
74
75
76
77 @Get("/projects")
78 public CompletableFuture<List<ProjectDto>> listProjects(@Param @Nullable String status, User user) {
79 final ContextAwareBlockingTaskExecutor executor =
80 ServiceRequestContext.current().blockingTaskExecutor();
81 if (status != null) {
82 checkStatusArgument(status);
83 return CompletableFuture.supplyAsync(() -> projectApiManager.listRemovedProjects().keySet()
84 .stream()
85 .map(ProjectDto::new)
86 .collect(toImmutableList()), executor);
87 }
88
89 return CompletableFuture.supplyAsync(() -> {
90 return projectApiManager.listProjects(user).values().stream()
91 .map(project -> DtoConverter.convert(project, getUserRole(project, user)))
92 .collect(toImmutableList());
93 }, executor);
94 }
95
96 private static ProjectRole getUserRole(Project project, User user) {
97 if (user.isSystemAdmin()) {
98 return ProjectRole.OWNER;
99 }
100
101 final ProjectMetadata metadata = project.metadata();
102 if (metadata == null) {
103
104 return ProjectRole.GUEST;
105 }
106
107 ProjectRole role = null;
108 if (user instanceof UserWithToken) {
109 final String appId = ((UserWithToken) user).token().appId();
110 final TokenRegistration tokenRegistration = metadata.tokens().get(appId);
111 if (tokenRegistration != null) {
112 role = tokenRegistration.role();
113 }
114 } else {
115 final Member member = metadata.memberOrDefault(user.id(), null);
116 if (member != null) {
117 role = member.role();
118 }
119 }
120
121 if (role == null) {
122 role = ProjectRole.GUEST;
123 }
124
125 return role;
126 }
127
128
129
130
131
132
133 @Post("/projects")
134 @StatusCode(201)
135 @ResponseConverter(CreateApiResponseConverter.class)
136 public CompletableFuture<ProjectDto> createProject(CreateProjectRequest request, Author author, User user) {
137 return projectApiManager.createProject(request.name(), author).handle(returnOrThrow(() -> {
138 final Project project = projectApiManager.getProject(request.name(), user);
139 return DtoConverter.convert(project, ProjectRole.OWNER);
140 }));
141 }
142
143
144
145
146
147
148 @Get("/projects/{projectName}")
149 @RequiresProjectRole(ProjectRole.MEMBER)
150 public CompletableFuture<ProjectMetadata> getProjectMetadata(@Param String projectName) {
151 return projectApiManager.getProjectMetadata(projectName);
152 }
153
154
155
156
157
158
159 @Delete("/projects/{projectName}")
160 @RequiresProjectRole(ProjectRole.OWNER)
161 public CompletableFuture<Void> removeProject(Project project, Author author) {
162 return projectApiManager.removeProject(project.name(), author);
163 }
164
165
166
167
168
169
170 @Delete("/projects/{projectName}/removed")
171 @RequiresProjectRole(ProjectRole.OWNER)
172 public CompletableFuture<Void> purgeProject(@Param String projectName, Author author) {
173 return projectApiManager.purgeProject(projectName, author);
174 }
175
176
177
178
179
180
181
182
183 @Consumes("application/json-patch+json")
184 @Patch("/projects/{projectName}")
185 @RequiresSystemAdministrator
186 public CompletableFuture<ProjectDto> patchProject(@Param String projectName, JsonNode node, Author author,
187 User user) {
188 checkUnremoveArgument(node);
189 return projectApiManager.unremoveProject(projectName, author)
190 .handle(returnOrThrow(() -> DtoConverter.convert(
191 projectApiManager.getProject(projectName, user), ProjectRole.OWNER)));
192 }
193 }