diff --git a/src/main/java/com/fowoco/server/common/config/SecurityConfig.java b/src/main/java/com/fowoco/server/common/config/SecurityConfig.java index 4797307b..dbd4e708 100644 --- a/src/main/java/com/fowoco/server/common/config/SecurityConfig.java +++ b/src/main/java/com/fowoco/server/common/config/SecurityConfig.java @@ -109,8 +109,8 @@ public SecurityFilterChain applicationSecurityFilterChain( "/api/v1/auth/password-resets" ).permitAll() .requestMatchers("/error").permitAll() - .requestMatchers(HttpMethod.GET, "/public/worker-links/**").permitAll() - .requestMatchers(HttpMethod.POST, "/public/worker-links/**").permitAll() + .requestMatchers(HttpMethod.GET, "/api/v1/public/worker-links/**").permitAll() + .requestMatchers(HttpMethod.POST, "/api/v1/public/worker-links/**").permitAll() .requestMatchers(HttpMethod.GET, "/api/v1/**") .hasAnyRole("ADMIN", "HR", "VIEWER") .requestMatchers(HttpMethod.HEAD, "/api/v1/**") diff --git a/src/main/java/com/fowoco/server/demo/infrastructure/seed/DemoOperationalSeedCatalog.java b/src/main/java/com/fowoco/server/demo/infrastructure/seed/DemoOperationalSeedCatalog.java index 5118e480..d60b9395 100644 --- a/src/main/java/com/fowoco/server/demo/infrastructure/seed/DemoOperationalSeedCatalog.java +++ b/src/main/java/com/fowoco/server/demo/infrastructure/seed/DemoOperationalSeedCatalog.java @@ -50,8 +50,8 @@ final class DemoOperationalSeedCatalog { UUID.fromString("96000000-0000-0000-0000-000000000027"), UUID.fromString("96000000-0000-0000-0000-000000000028"), UUID.fromString("96000000-0000-0000-0000-000000000045"), - UUID.fromString("96000000-0000-0000-0000-000000000083"), - UUID.fromString("96000000-0000-0000-0000-000000000094") + UUID.fromString("96000000-0000-0000-0000-000000000089"), + UUID.fromString("96000000-0000-0000-0000-000000000100") ); private static final UUID RETIRED_GOLDEN_FLOW_DRAFT_ID = UUID.fromString("94700000-0000-0000-0000-000000000002"); @@ -221,12 +221,12 @@ private void verifyDefinitions() { requireSize(demoDocuments, 83, "Demo Company document"); requireSize(testDocuments, 8, "Test Company document"); requireSize(demoChecklists, 60, "Demo Company checklist item"); - requireSize(demoApprovals, 12, "Demo Company approval request"); - requireSize(demoTransitions, 48, "Demo Company task transition"); + requireSize(demoApprovals, 15, "Demo Company approval request"); + requireSize(demoTransitions, 54, "Demo Company task transition"); requireSize(demoExternalSubmissions, 6, "Demo Company external submission"); requireSize(demoEvidence, 10, "Demo Company completion evidence"); requireSize(demoDocumentRequestDrafts, 4, "Demo Company document request draft"); - requireSize(demoAudits, 88, "Demo Company audit event"); + requireSize(demoAudits, 94, "Demo Company audit event"); requireSize(testAudits, 8, "Test Company audit event"); requireDistribution( demoTasks.stream().map(TaskSeed::taskType).toList(), @@ -264,7 +264,7 @@ private void verifyDefinitions() { demoApprovals.stream().map(ApprovalSeed::status).toList(), Map.of( ApprovalStatus.PENDING, 3L, - ApprovalStatus.APPROVED, 7L, + ApprovalStatus.APPROVED, 10L, ApprovalStatus.REJECTED, 1L, ApprovalStatus.INVALIDATED, 1L ), @@ -283,7 +283,7 @@ private void verifyDefinitions() { requireDistribution( demoAudits.stream().map(AuditSeed::actorType).toList(), Map.of( - ActorType.HR_USER, 77L, + ActorType.HR_USER, 83L, ActorType.AI_AGENT, 2L, ActorType.SYSTEM_RULE, 6L, ActorType.WORKER_LINK, 3L diff --git a/src/main/java/com/fowoco/server/demo/infrastructure/seed/DemoTaskWorkflowSeedCatalog.java b/src/main/java/com/fowoco/server/demo/infrastructure/seed/DemoTaskWorkflowSeedCatalog.java index 5dea3bd7..caa0e003 100644 --- a/src/main/java/com/fowoco/server/demo/infrastructure/seed/DemoTaskWorkflowSeedCatalog.java +++ b/src/main/java/com/fowoco/server/demo/infrastructure/seed/DemoTaskWorkflowSeedCatalog.java @@ -77,7 +77,10 @@ static List demoApprovals(List tasks) { approval(11, tasks, 23, ApprovalStatus.APPROVED, "신청 결과 확인 후 승인"), approval(12, tasks, 9, ApprovalStatus.REJECTED, "필수 고용 정보 보완 필요"), approval(13, tasks, 1, ApprovalStatus.INVALIDATED, - "마감일 변경으로 기존 승인 snapshot을 무효화") + "마감일 변경으로 기존 승인 snapshot을 무효화"), + approval(14, tasks, 3, ApprovalStatus.APPROVED, "여권 사본 요청 전 승인 확인"), + approval(15, tasks, 10, ApprovalStatus.APPROVED, "체류서류 요청 전 승인 확인"), + approval(16, tasks, 11, ApprovalStatus.APPROVED, "외국인등록증 요청 전 승인 확인") ); } @@ -282,6 +285,8 @@ private static List transitionPath(int taskNumber) { case 3, 8, 10, 11 -> List.of( TaskStatus.NEEDS_INFO, TaskStatus.DRAFT, + TaskStatus.READY_FOR_REVIEW, + TaskStatus.APPROVED, TaskStatus.WAITING_WORKER ); case 4, 18, 19 -> List.of( diff --git a/src/main/java/com/fowoco/server/workerlink/api/WorkerLinkDocumentController.java b/src/main/java/com/fowoco/server/workerlink/api/WorkerLinkDocumentController.java index 18e305ff..dde6990a 100644 --- a/src/main/java/com/fowoco/server/workerlink/api/WorkerLinkDocumentController.java +++ b/src/main/java/com/fowoco/server/workerlink/api/WorkerLinkDocumentController.java @@ -58,7 +58,7 @@ public WorkerLinkDocumentController(WorkerLinkDocumentService workerLinkDocument @ApiResponse(responseCode = "429", description = "요청 과다") }) @PostMapping( - path = "/public/worker-links/{token}/documents", + path = "/api/v1/public/worker-links/{token}/documents", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE ) diff --git a/src/main/java/com/fowoco/server/workerlink/api/WorkerLinkViewController.java b/src/main/java/com/fowoco/server/workerlink/api/WorkerLinkViewController.java index d90f8d17..d698558a 100644 --- a/src/main/java/com/fowoco/server/workerlink/api/WorkerLinkViewController.java +++ b/src/main/java/com/fowoco/server/workerlink/api/WorkerLinkViewController.java @@ -21,7 +21,7 @@ @Tag(name = "Worker Link (Public)", description = "근로자 공개 안내·제출") @RestController -@RequestMapping("/public/worker-links/{token}") +@RequestMapping("/api/v1/public/worker-links/{token}") public class WorkerLinkViewController { private final WorkerLinkViewService workerLinkViewService; diff --git a/src/main/java/com/fowoco/server/workerlink/api/WorkerResponseController.java b/src/main/java/com/fowoco/server/workerlink/api/WorkerResponseController.java index b5572793..23c05fa2 100644 --- a/src/main/java/com/fowoco/server/workerlink/api/WorkerResponseController.java +++ b/src/main/java/com/fowoco/server/workerlink/api/WorkerResponseController.java @@ -53,7 +53,7 @@ public WorkerResponseController(WorkerResponseService workerResponseService) { @ApiResponse(responseCode = "429", description = "요청 과다") }) @PostMapping( - path = "/public/worker-links/{token}/responses", + path = "/api/v1/public/worker-links/{token}/responses", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE ) diff --git a/src/test/java/com/fowoco/server/auth/infrastructure/seed/DemoAuthSeedIntegrationTest.java b/src/test/java/com/fowoco/server/auth/infrastructure/seed/DemoAuthSeedIntegrationTest.java index 52b0d679..b2b1e7f1 100644 --- a/src/test/java/com/fowoco/server/auth/infrastructure/seed/DemoAuthSeedIntegrationTest.java +++ b/src/test/java/com/fowoco/server/auth/infrastructure/seed/DemoAuthSeedIntegrationTest.java @@ -120,12 +120,12 @@ class DemoAuthSeedIntegrationTest { Map.entry("worker_document", 83), Map.entry("stored_file", 3), Map.entry("task_checklist_item", 60), - Map.entry("approval_request", 12), - Map.entry("task_transition_history", 48), + Map.entry("approval_request", 15), + Map.entry("task_transition_history", 54), Map.entry("external_submission", 6), Map.entry("task_evidence", 10), Map.entry("document_request_draft", 4), - Map.entry("audit_event", 88) + Map.entry("audit_event", 94) ); private static final Map EXPECTED_TEST_COUNTS = Map.of( "user_account", 3, @@ -271,11 +271,11 @@ private void assertExactCountsAndDistributions() { ); assertThat(distribution("approval_request", "status", COMPANY_ID)) .containsExactlyInAnyOrderEntriesOf( - Map.of("PENDING", 3, "APPROVED", 7, "REJECTED", 1, "INVALIDATED", 1) + Map.of("PENDING", 3, "APPROVED", 10, "REJECTED", 1, "INVALIDATED", 1) ); assertThat(distribution("audit_event", "actor_type", COMPANY_ID)) .containsExactlyInAnyOrderEntriesOf( - Map.of("HR_USER", 77, "AI_AGENT", 2, "SYSTEM_RULE", 6, "WORKER_LINK", 3) + Map.of("HR_USER", 83, "AI_AGENT", 2, "SYSTEM_RULE", 6, "WORKER_LINK", 3) ); assertThat(jdbcTemplate.queryForObject( @@ -952,7 +952,7 @@ private void assertFrontendRelevantApiResultsAndTenantIsolation() throws Excepti HttpResponse testAudits = authorizedGet("/api/v1/audit-events?limit=100", testToken); assertOk(demoAudits); assertOk(testAudits); - assertThat(JsonPath.>read(demoAudits.body(), "$.items")).hasSize(88); + assertThat(JsonPath.>read(demoAudits.body(), "$.items")).hasSize(94); assertThat(JsonPath.>read(testAudits.body(), "$.items")).hasSize(8); assertTenantApiCount(testToken, "/api/v1/workers?size=100", 5); diff --git a/src/test/java/com/fowoco/server/common/security/PostgreSqlRestrictedRoleHttpE2ETest.java b/src/test/java/com/fowoco/server/common/security/PostgreSqlRestrictedRoleHttpE2ETest.java index b5c77161..d9489444 100644 --- a/src/test/java/com/fowoco/server/common/security/PostgreSqlRestrictedRoleHttpE2ETest.java +++ b/src/test/java/com/fowoco/server/common/security/PostgreSqlRestrictedRoleHttpE2ETest.java @@ -36,7 +36,7 @@ class PostgreSqlRestrictedRoleHttpE2ETest { private static final String COOKIE_NAME = "fowoco_refresh_token"; - private static final String PROBE_PATH = "/public/worker-links/rls-test/probes"; + private static final String PROBE_PATH = "/api/v1/public/worker-links/rls-test/probes"; private static final List RLS_TABLES = List.of( "company", "user_account", @@ -259,7 +259,7 @@ void refreshRejectsUnknownExpiredAndRevokedTokensWithOneSafeContract() @Test void workerLinkBootstrapAcceptsOnlyActiveRegisteredLinks() throws Exception { HttpResponse active = get( - "/public/worker-links/" + "/api/v1/public/worker-links/" + PostgreSqlRestrictedRoleHttpDataFixture.ACTIVE_WORKER_LINK_TOKEN, null ); @@ -277,7 +277,7 @@ void workerLinkBootstrapAcceptsOnlyActiveRegisteredLinks() throws Exception { ); HttpResponse responseWrite = postJson( - "/public/worker-links/" + "/api/v1/public/worker-links/" + PostgreSqlRestrictedRoleHttpDataFixture.ACTIVE_WORKER_LINK_TOKEN + "/responses", """ @@ -298,16 +298,16 @@ void workerLinkBootstrapAcceptsOnlyActiveRegisteredLinks() throws Exception { ); assertThat(get( - "/public/worker-links/" + "/api/v1/public/worker-links/" + PostgreSqlRestrictedRoleHttpDataFixture.EXPIRED_WORKER_LINK_TOKEN, null ).statusCode()).isEqualTo(410); assertThat(get( - "/public/worker-links/" + "/api/v1/public/worker-links/" + PostgreSqlRestrictedRoleHttpDataFixture.REVOKED_WORKER_LINK_TOKEN, null ).statusCode()).isEqualTo(410); - assertThat(get("/public/worker-links/unregistered-rls-http-token", null).statusCode()) + assertThat(get("/api/v1/public/worker-links/unregistered-rls-http-token", null).statusCode()) .isEqualTo(410); } diff --git a/src/test/java/com/fowoco/server/demo/infrastructure/seed/DemoOperationalSeedCatalogTest.java b/src/test/java/com/fowoco/server/demo/infrastructure/seed/DemoOperationalSeedCatalogTest.java index 37460b5f..22f3d13e 100644 --- a/src/test/java/com/fowoco/server/demo/infrastructure/seed/DemoOperationalSeedCatalogTest.java +++ b/src/test/java/com/fowoco/server/demo/infrastructure/seed/DemoOperationalSeedCatalogTest.java @@ -69,15 +69,15 @@ void excludesGoldenFlowOperationalDataWithoutRenumberingShowcaseFixtures() { .containsExactlyInAnyOrderElementsOf(idsExcept( "94300000-0000-0000-0000-000000000", 1, - 13, + 16, Set.of(2) )); assertThat(catalog.demoTransitions().stream().map(seed -> seed.transitionId())) .containsExactlyInAnyOrderElementsOf(idsExcept( "94400000-0000-0000-0000-000000000", 1, - 52, - IntStream.rangeClosed(13, 16).boxed().collect(Collectors.toSet()) + 60, + IntStream.rangeClosed(15, 20).boxed().collect(Collectors.toSet()) )); assertThat(catalog.demoDocumentRequestDrafts().stream().map(seed -> seed.draftId())) .containsExactlyInAnyOrderElementsOf(idsExcept( @@ -90,8 +90,8 @@ void excludesGoldenFlowOperationalDataWithoutRenumberingShowcaseFixtures() { .containsExactlyInAnyOrderElementsOf(idsExcept( "96000000-0000-0000-0000-000000000", 1, - 96, - Set.of(24, 25, 26, 27, 28, 45, 83, 94) + 102, + Set.of(24, 25, 26, 27, 28, 45, 89, 100) )); Set activeTaskIds = catalog.demoTasks().stream() diff --git a/src/test/java/com/fowoco/server/demo/infrastructure/seed/DemoSeedPostgreSqlApplicationIntegrationTest.java b/src/test/java/com/fowoco/server/demo/infrastructure/seed/DemoSeedPostgreSqlApplicationIntegrationTest.java index de5991ae..d6e032a2 100644 --- a/src/test/java/com/fowoco/server/demo/infrastructure/seed/DemoSeedPostgreSqlApplicationIntegrationTest.java +++ b/src/test/java/com/fowoco/server/demo/infrastructure/seed/DemoSeedPostgreSqlApplicationIntegrationTest.java @@ -36,12 +36,12 @@ class DemoSeedPostgreSqlApplicationIntegrationTest { Map.entry("worker_document", 83), Map.entry("stored_file", 3), Map.entry("task_checklist_item", 60), - Map.entry("approval_request", 12), - Map.entry("task_transition_history", 48), + Map.entry("approval_request", 15), + Map.entry("task_transition_history", 54), Map.entry("external_submission", 6), Map.entry("task_evidence", 10), Map.entry("document_request_draft", 4), - Map.entry("audit_event", 88) + Map.entry("audit_event", 94) ); @TempDir diff --git a/src/test/java/com/fowoco/server/workerlink/WorkerLinkSecurityIntegrationTest.java b/src/test/java/com/fowoco/server/workerlink/WorkerLinkSecurityIntegrationTest.java index bf7ae808..b0754b9e 100644 --- a/src/test/java/com/fowoco/server/workerlink/WorkerLinkSecurityIntegrationTest.java +++ b/src/test/java/com/fowoco/server/workerlink/WorkerLinkSecurityIntegrationTest.java @@ -137,7 +137,7 @@ void fullFlow_issueViewUploadRespond_succeeds() throws Exception { ); assertThat(markSentResponse.statusCode()).isEqualTo(200); - HttpResponse viewResponse = getJson("/public/worker-links/" + rawToken, null); + HttpResponse viewResponse = getJson("/api/v1/public/worker-links/" + rawToken, null); assertThat(viewResponse.statusCode()).isEqualTo(200); assertThat(viewResponse.headers().firstValue("Cache-Control")).contains("no-store"); assertThat(JsonPath.read(viewResponse.body(), "$.guidance")) @@ -167,7 +167,7 @@ void fullFlow_issueViewUploadRespond_succeeds() throws Exception { assertThat(retryUploadId).isEqualTo(firstUploadId); HttpResponse responseSubmit = postJson( - "/public/worker-links/" + rawToken + "/responses", + "/api/v1/public/worker-links/" + rawToken + "/responses", """ {"response_type":"DOCUMENT_SUBMITTED","upload_ids":["%s"],"idempotency_key":"key-1"} """.formatted(uploadId), @@ -325,7 +325,7 @@ void activeWorkerLinkWithoutDraftReturnsContentNotReady() throws Exception { String taskId = createApprovedTask(hrToken, workerId); String workerUrl = issueWorkerLink(hrToken, taskId, "content-not-ready-key"); - HttpResponse response = getJson("/public/worker-links/" + workerUrl, null); + HttpResponse response = getJson("/api/v1/public/worker-links/" + workerUrl, null); assertThat(response.statusCode()).isEqualTo(409); assertThat(JsonPath.read(response.body(), "$.code")) @@ -340,7 +340,7 @@ void hrCanListAndMarkWorkerResponsesReviewed() throws Exception { String workerUrl = issueWorkerLink(hrToken, taskId, "response-management-key"); HttpResponse submitResponse = postJson( - "/public/worker-links/" + workerUrl + "/responses", + "/api/v1/public/worker-links/" + workerUrl + "/responses", """ {"response_type":"QUESTION","message":"여권의 어느 면을 제출하나요?","idempotency_key":"question-key"} """, @@ -422,7 +422,7 @@ void hrCanInspectAndAdoptSubmittedFilesThenResumeTask() throws Exception { String contractFileId = JsonPath.read(contractUpload.body(), "$.upload_id"); HttpResponse submitResponse = postJson( - "/public/worker-links/" + rawToken + "/responses", + "/api/v1/public/worker-links/" + rawToken + "/responses", """ { "response_type":"DOCUMENT_SUBMITTED", @@ -939,12 +939,12 @@ void issueRejectsOtherCompanyTask() throws Exception { @Test void viewReturns410ForNonExistentToken() throws Exception { - HttpResponse viewResponse = getJson("/public/worker-links/nonexistenttoken12345", null); + HttpResponse viewResponse = getJson("/api/v1/public/worker-links/nonexistenttoken12345", null); assertThat(viewResponse.statusCode()).isEqualTo(410); } @Test void documentsEndpointAllowsIdempotencyKeyHeaderInCors() throws Exception { - HttpRequest request = HttpRequest.newBuilder(uri("/public/worker-links/test-token/documents")) + HttpRequest request = HttpRequest.newBuilder(uri("/api/v1/public/worker-links/test-token/documents")) .header("Origin", "http://localhost:3000") .header("Access-Control-Request-Method", "POST") .header("Access-Control-Request-Headers", "Idempotency-Key") @@ -1074,7 +1074,7 @@ private HttpResponse uploadFile(String token, String filename, String mi writeFieldPart(out, "clientRequestId", UUID.randomUUID().toString()); out.write(("--" + BOUNDARY + "--\r\n").getBytes(StandardCharsets.UTF_8)); - HttpRequest request = HttpRequest.newBuilder(uri("/public/worker-links/" + token + "/documents")) + HttpRequest request = HttpRequest.newBuilder(uri("/api/v1/public/worker-links/" + token + "/documents")) .header(HttpHeaders.CONTENT_TYPE, "multipart/form-data; boundary=" + BOUNDARY) .POST(HttpRequest.BodyPublishers.ofByteArray(out.toByteArray())) .build(); @@ -1094,7 +1094,7 @@ private HttpResponse uploadFileAsType( writeFieldPart(out, "documentType", documentType); out.write(("--" + BOUNDARY + "--\r\n").getBytes(StandardCharsets.UTF_8)); - HttpRequest request = HttpRequest.newBuilder(uri("/public/worker-links/" + token + "/documents")) + HttpRequest request = HttpRequest.newBuilder(uri("/api/v1/public/worker-links/" + token + "/documents")) .header(HttpHeaders.CONTENT_TYPE, "multipart/form-data; boundary=" + BOUNDARY) .POST(HttpRequest.BodyPublishers.ofByteArray(out.toByteArray())) .build(); @@ -1109,7 +1109,7 @@ private HttpResponse uploadFileWithFixedClientRequestId( writeFieldPart(out, "clientRequestId", clientRequestId); out.write(("--" + BOUNDARY + "--\r\n").getBytes(StandardCharsets.UTF_8)); - HttpRequest request = HttpRequest.newBuilder(uri("/public/worker-links/" + token + "/documents")) + HttpRequest request = HttpRequest.newBuilder(uri("/api/v1/public/worker-links/" + token + "/documents")) .header(HttpHeaders.CONTENT_TYPE, "multipart/form-data; boundary=" + BOUNDARY) .POST(HttpRequest.BodyPublishers.ofByteArray(out.toByteArray())) .build();