Skip to content

Commit add1059

Browse files
committed
Enhance project and quality gate request handling with additional JSON properties and validation
1 parent e42baab commit add1059

10 files changed

Lines changed: 104 additions & 77 deletions

File tree

java-ecosystem/libs/core/src/main/java/module-info.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@
8888
exports org.rostilos.codecrow.core.model.branch;
8989
exports org.rostilos.codecrow.core.persistence.repository.branch;
9090
exports org.rostilos.codecrow.core.model.project.config;
91+
opens org.rostilos.codecrow.core.model.project.config to com.fasterxml.jackson.databind;
92+
9193
exports org.rostilos.codecrow.core.model.analysis;
9294
exports org.rostilos.codecrow.core.persistence.repository.analysis;
9395

java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/dto/qualitygate/QualityGateDTO.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.rostilos.codecrow.core.dto.qualitygate;
22

3+
import com.fasterxml.jackson.annotation.JsonProperty;
34
import org.rostilos.codecrow.core.model.qualitygate.QualityGate;
45

56
import java.time.OffsetDateTime;
@@ -40,7 +41,9 @@ public static QualityGateDTO fromEntity(QualityGate entity) {
4041
public String getDescription() { return description; }
4142
public void setDescription(String description) { this.description = description; }
4243

44+
@JsonProperty("isDefault")
4345
public boolean isDefault() { return isDefault; }
46+
@JsonProperty("isDefault")
4447
public void setDefault(boolean isDefault) { this.isDefault = isDefault; }
4548

4649
public boolean isActive() { return active; }

java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/persistence/repository/workspace/WorkspaceMemberRepository.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import org.rostilos.codecrow.core.model.workspace.Workspace;
77
import org.rostilos.codecrow.core.model.workspace.WorkspaceMember;
8+
import org.springframework.data.jpa.repository.EntityGraph;
89
import org.springframework.data.jpa.repository.JpaRepository;
910
import org.springframework.data.jpa.repository.Modifying;
1011
import org.springframework.data.jpa.repository.Query;
@@ -15,6 +16,7 @@
1516
public interface WorkspaceMemberRepository extends JpaRepository<WorkspaceMember, Long> {
1617
Optional<WorkspaceMember> findByWorkspaceIdAndUserId(Long workspaceId, Long userId);
1718
java.util.List<WorkspaceMember> findByUser_Id(Long userId);
19+
@EntityGraph(attributePaths = "user")
1820
java.util.List<WorkspaceMember> findByWorkspace_Id(Long workspaceId);
1921
Long countByWorkspace_Id(Long workspaceId);
2022

@@ -25,4 +27,4 @@ public interface WorkspaceMemberRepository extends JpaRepository<WorkspaceMember
2527
@Query("SELECT wm.workspace FROM WorkspaceMember wm " +
2628
"WHERE wm.user.id = :userId AND wm.status = 'ACTIVE'")
2729
List<Workspace> findActiveWorkspacesByUserId(@Param("userId") Long userId);
28-
}
30+
}

java-ecosystem/libs/security/src/main/java/module-info.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@
2525
opens org.rostilos.codecrow.security.pipelineagent.jwt to spring.core, spring.beans, spring.context;
2626
opens org.rostilos.codecrow.security.jwt.utils to spring.core, spring.beans, spring.context;
2727
opens org.rostilos.codecrow.security.service to spring.core, spring.beans, spring.context;
28-
opens org.rostilos.codecrow.security.web to spring.core, spring.beans, spring.context;
28+
opens org.rostilos.codecrow.security.web;
2929
opens org.rostilos.codecrow.security.web.jwt to spring.core, spring.beans, spring.context;
3030
}

java-ecosystem/services/web-server/src/it/java/org/rostilos/codecrow/webserver/ProjectControllerIT.java

Lines changed: 56 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,15 @@ void setupWorkspace() {
3838
// List Projects
3939
// ───────────────────────────────────────────────
4040
@Nested
41-
@DisplayName("GET /api/{workspace}/project_list")
41+
@DisplayName("GET /api/{workspace}/project/project_list")
4242
class ListProjects {
4343

4444
@Test
4545
@DisplayName("List projects — empty workspace — returns empty list")
4646
void listProjects_empty_returnsEmptyList() {
4747
authenticatedRequest("projowner")
4848
.when()
49-
.get("/api/" + workspaceSlug + "/project_list")
49+
.get("/api/" + workspaceSlug + "/project/project_list")
5050
.then()
5151
.statusCode(200)
5252
.body("$", hasSize(0));
@@ -57,7 +57,7 @@ void listProjects_empty_returnsEmptyList() {
5757
void listProjects_unauthenticated_returns401() {
5858
unauthenticatedRequest()
5959
.when()
60-
.get("/api/" + workspaceSlug + "/project_list")
60+
.get("/api/" + workspaceSlug + "/project/project_list")
6161
.then()
6262
.statusCode(401);
6363
}
@@ -69,7 +69,7 @@ void listProjects_nonMember_returns403() {
6969

7070
authenticatedRequest("projstranger")
7171
.when()
72-
.get("/api/" + workspaceSlug + "/project_list")
72+
.get("/api/" + workspaceSlug + "/project/project_list")
7373
.then()
7474
.statusCode(403);
7575
}
@@ -79,15 +79,15 @@ void listProjects_nonMember_returns403() {
7979
// Paginated Projects
8080
// ───────────────────────────────────────────────
8181
@Nested
82-
@DisplayName("GET /api/{workspace}/projects")
82+
@DisplayName("GET /api/{workspace}/project/projects")
8383
class PaginatedProjects {
8484

8585
@Test
8686
@DisplayName("Paginated projects — with default params")
8787
void paginatedProjects_defaults_returnsPage() {
8888
authenticatedRequest("projowner")
8989
.when()
90-
.get("/api/" + workspaceSlug + "/projects")
90+
.get("/api/" + workspaceSlug + "/project/projects")
9191
.then()
9292
.statusCode(200);
9393
}
@@ -99,7 +99,7 @@ void paginatedProjects_customParams() {
9999
.queryParam("page", 0)
100100
.queryParam("size", 5)
101101
.when()
102-
.get("/api/" + workspaceSlug + "/projects")
102+
.get("/api/" + workspaceSlug + "/project/projects")
103103
.then()
104104
.statusCode(200);
105105
}
@@ -109,7 +109,7 @@ void paginatedProjects_customParams() {
109109
// Create Project
110110
// ───────────────────────────────────────────────
111111
@Nested
112-
@DisplayName("POST /api/{workspace}/project")
112+
@DisplayName("POST /api/{workspace}/project/create")
113113
class CreateProject {
114114

115115
@Test
@@ -118,19 +118,19 @@ void createProject_minimal_succeeds() {
118118
authenticatedRequest("projowner")
119119
.body("""
120120
{
121-
"projectName": "Test Project",
122-
"projectNamespace": "test-project",
121+
"name": "Test Project",
122+
"namespace": "test-project",
123123
"description": "A test project",
124-
"triggerMode": "MANUAL",
125-
"defaultBranch": "main"
124+
"creationMode": "MANUAL",
125+
"mainBranch": "main"
126126
}
127127
""")
128128
.when()
129-
.post("/api/" + workspaceSlug + "/project")
129+
.post("/api/" + workspaceSlug + "/project/create")
130130
.then()
131131
.statusCode(anyOf(is(200), is(201)))
132-
.body("projectName", equalTo("Test Project"))
133-
.body("projectNamespace", equalTo("test-project"));
132+
.body("name", equalTo("Test Project"))
133+
.body("namespace", equalTo("test-project"));
134134
}
135135

136136
@Test
@@ -139,12 +139,12 @@ void createProject_missingName_returns400() {
139139
authenticatedRequest("projowner")
140140
.body("""
141141
{
142-
"projectNamespace": "no-name-proj",
143-
"triggerMode": "MANUAL"
142+
"namespace": "no-name-proj",
143+
"creationMode": "MANUAL"
144144
}
145145
""")
146146
.when()
147-
.post("/api/" + workspaceSlug + "/project")
147+
.post("/api/" + workspaceSlug + "/project/create")
148148
.then()
149149
.statusCode(400);
150150
}
@@ -155,12 +155,12 @@ void createProject_missingNamespace_returns400() {
155155
authenticatedRequest("projowner")
156156
.body("""
157157
{
158-
"projectName": "No Namespace",
159-
"triggerMode": "MANUAL"
158+
"name": "No Namespace",
159+
"creationMode": "MANUAL"
160160
}
161161
""")
162162
.when()
163-
.post("/api/" + workspaceSlug + "/project")
163+
.post("/api/" + workspaceSlug + "/project/create")
164164
.then()
165165
.statusCode(400);
166166
}
@@ -171,13 +171,13 @@ void createProject_unauthenticated_returns401() {
171171
unauthenticatedRequest()
172172
.body("""
173173
{
174-
"projectName": "Hack Project",
175-
"projectNamespace": "hack-proj",
176-
"triggerMode": "MANUAL"
174+
"name": "Hack Project",
175+
"namespace": "hack-proj",
176+
"creationMode": "MANUAL"
177177
}
178178
""")
179179
.when()
180-
.post("/api/" + workspaceSlug + "/project")
180+
.post("/api/" + workspaceSlug + "/project/create")
181181
.then()
182182
.statusCode(401);
183183
}
@@ -190,13 +190,13 @@ void createProject_nonMember_returns403() {
190190
authenticatedRequest("projintruder")
191191
.body("""
192192
{
193-
"projectName": "Intruder Project",
194-
"projectNamespace": "intruder-proj",
195-
"triggerMode": "MANUAL"
193+
"name": "Intruder Project",
194+
"namespace": "intruder-proj",
195+
"creationMode": "MANUAL"
196196
}
197197
""")
198198
.when()
199-
.post("/api/" + workspaceSlug + "/project")
199+
.post("/api/" + workspaceSlug + "/project/create")
200200
.then()
201201
.statusCode(403);
202202
}
@@ -207,29 +207,29 @@ void createProject_duplicateNamespace_returnsError() {
207207
authenticatedRequest("projowner")
208208
.body("""
209209
{
210-
"projectName": "First Project",
211-
"projectNamespace": "dup-ns",
212-
"triggerMode": "MANUAL",
213-
"defaultBranch": "main"
210+
"name": "First Project",
211+
"namespace": "dup-ns",
212+
"creationMode": "MANUAL",
213+
"mainBranch": "main"
214214
}
215215
""")
216216
.when()
217-
.post("/api/" + workspaceSlug + "/project")
217+
.post("/api/" + workspaceSlug + "/project/create")
218218
.then()
219219
.statusCode(anyOf(is(200), is(201)));
220220

221221
// Duplicate namespace
222222
authenticatedRequest("projowner")
223223
.body("""
224224
{
225-
"projectName": "Second Project",
226-
"projectNamespace": "dup-ns",
227-
"triggerMode": "MANUAL",
228-
"defaultBranch": "main"
225+
"name": "Second Project",
226+
"namespace": "dup-ns",
227+
"creationMode": "MANUAL",
228+
"mainBranch": "main"
229229
}
230230
""")
231231
.when()
232-
.post("/api/" + workspaceSlug + "/project")
232+
.post("/api/" + workspaceSlug + "/project/create")
233233
.then()
234234
.statusCode(anyOf(is(400), is(409), is(500)));
235235
}
@@ -250,15 +250,15 @@ void createProject() {
250250
authenticatedRequest("projowner")
251251
.body("""
252252
{
253-
"projectName": "CRUD Project",
254-
"projectNamespace": "%s",
253+
"name": "CRUD Project",
254+
"namespace": "%s",
255255
"description": "Project for CRUD tests",
256-
"triggerMode": "MANUAL",
257-
"defaultBranch": "main"
256+
"creationMode": "MANUAL",
257+
"mainBranch": "main"
258258
}
259259
""".formatted(projectNs))
260260
.when()
261-
.post("/api/" + workspaceSlug + "/project")
261+
.post("/api/" + workspaceSlug + "/project/create")
262262
.then()
263263
.statusCode(anyOf(is(200), is(201)));
264264
}
@@ -269,22 +269,22 @@ void updateProject_changeName_succeeds() {
269269
authenticatedRequest("projowner")
270270
.body("""
271271
{
272-
"projectName": "Updated CRUD Project"
272+
"name": "Updated CRUD Project"
273273
}
274274
""")
275275
.when()
276276
.patch("/api/" + workspaceSlug + "/project/" + projectNs)
277277
.then()
278278
.statusCode(200)
279-
.body("projectName", equalTo("Updated CRUD Project"));
279+
.body("name", equalTo("Updated CRUD Project"));
280280
}
281281

282282
@Test
283283
@DisplayName("List projects after creation — contains created project")
284284
void listProjects_afterCreate_containsProject() {
285285
authenticatedRequest("projowner")
286286
.when()
287-
.get("/api/" + workspaceSlug + "/project_list")
287+
.get("/api/" + workspaceSlug + "/project/project_list")
288288
.then()
289289
.statusCode(200)
290290
.body("$", hasSize(greaterThanOrEqualTo(1)));
@@ -296,7 +296,7 @@ void generateProjectToken_succeeds() {
296296
authenticatedRequest("projowner")
297297
.body("""
298298
{
299-
"tokenName": "ci-token"
299+
"name": "ci-token"
300300
}
301301
""")
302302
.when()
@@ -313,15 +313,15 @@ void listProjectTokens_succeeds() {
313313
authenticatedRequest("projowner")
314314
.body("""
315315
{
316-
"tokenName": "list-token"
316+
"name": "list-token"
317317
}
318318
""")
319319
.when()
320320
.post("/api/" + workspaceSlug + "/project/" + projectNs + "/token/generate");
321321

322322
authenticatedRequest("projowner")
323323
.when()
324-
.get("/api/" + workspaceSlug + "/project/" + projectNs + "/tokens")
324+
.get("/api/" + workspaceSlug + "/project/" + projectNs + "/token")
325325
.then()
326326
.statusCode(200)
327327
.body("$", hasSize(greaterThanOrEqualTo(1)));
@@ -343,14 +343,14 @@ void createProject() {
343343
authenticatedRequest("projowner")
344344
.body("""
345345
{
346-
"projectName": "Config Project",
347-
"projectNamespace": "%s",
348-
"triggerMode": "MANUAL",
349-
"defaultBranch": "main"
346+
"name": "Config Project",
347+
"namespace": "%s",
348+
"creationMode": "MANUAL",
349+
"mainBranch": "main"
350350
}
351351
""".formatted(projectNs))
352352
.when()
353-
.post("/api/" + workspaceSlug + "/project")
353+
.post("/api/" + workspaceSlug + "/project/create")
354354
.then()
355355
.statusCode(anyOf(is(200), is(201)));
356356
}
@@ -360,7 +360,7 @@ void createProject() {
360360
void getCommentCommandsConfig_succeeds() {
361361
authenticatedRequest("projowner")
362362
.when()
363-
.get("/api/" + workspaceSlug + "/project/" + projectNs + "/comment-commands")
363+
.get("/api/" + workspaceSlug + "/project/" + projectNs + "/comment-commands-config")
364364
.then()
365365
.statusCode(200);
366366
}

0 commit comments

Comments
 (0)