Skip to content

Conversation

@catturtle123
Copy link
Contributor

@catturtle123 catturtle123 commented Jan 30, 2026

Summary

  • DELETE /api/v1/members/me 회원 탈퇴 API 추가
  • 회원 삭제 시 연관 데이터(답변, 회고 등) 유지 (ON DELETE SET NULL)
  • Terraform S3 backend 마이그레이션

Changes

  • member_idOption<i64>로 변경하여 탈퇴 멤버 처리
  • 트랜잭션으로 refresh_token과 member 삭제 원자적 처리
  • PDF 내보내기에서 탈퇴 멤버 표시 로직 추가

Test plan

  • 회원 탈퇴 API 호출 테스트
  • 탈퇴 후 연관 데이터(답변, 회고) 유지 확인
  • PDF 내보내기 시 탈퇴 멤버 표시 확인

DB Migration Required

-- member_response 테이블
ALTER TABLE member_response 
  DROP FOREIGN KEY member_response_ibfk_1,
  MODIFY member_id BIGINT NULL,
  ADD CONSTRAINT member_response_ibfk_1 
    FOREIGN KEY (member_id) REFERENCES member(member_id) 
    ON DELETE SET NULL ON UPDATE NO ACTION;

-- member_retro 테이블
ALTER TABLE member_retro 
  DROP FOREIGN KEY member_retro_ibfk_1,
  MODIFY member_id BIGINT NULL,
  ADD CONSTRAINT member_retro_ibfk_1 
    FOREIGN KEY (member_id) REFERENCES member(member_id) 
    ON DELETE SET NULL ON UPDATE NO ACTION;

-- member_retro_room 테이블
ALTER TABLE member_retro_room 
  DROP FOREIGN KEY member_retro_room_ibfk_1,
  MODIFY member_id BIGINT NULL,
  ADD CONSTRAINT member_retro_room_ibfk_1 
    FOREIGN KEY (member_id) REFERENCES member(member_id) 
    ON DELETE SET NULL ON UPDATE NO ACTION;

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Users can delete their accounts via a new withdraw endpoint (DELETE /api/v1/members/me) with documented success response schema.
  • Bug Fixes

    • Related records now preserve references (shown as "탈퇴한 멤버") instead of being cascaded away.
    • New not-found error path and code for missing members.
  • Tests

    • Added tests for success, missing/invalid authorization, and user-not-found scenarios.

✏️ Tip: You can customize this high-level summary in your review settings.

- DELETE /api/v1/members/me 엔드포인트 추가
- 회원 삭제 시 연관 데이터(답변, 회고 등) 유지 (ON DELETE SET NULL)
- member_id를 Option<i64>로 변경하여 탈퇴 멤버 처리
- 트랜잭션으로 refresh_token과 member 삭제 원자적 처리
- Terraform S3 backend 마이그레이션 (v2)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@catturtle123 catturtle123 self-assigned this Jan 30, 2026
@github-actions
Copy link

Terraform Format 🖌 failure

Terraform Init ⚙️ success

Terraform Validate 🤖 success

Terraform Plan 📖 success

Show Plan
Acquiring state lock. This may take a few moments...
data.aws_ami.ubuntu_2404: Reading...
aws_vpc.main: Refreshing state... [id=vpc-06e337fe8c72d274b]
aws_db_parameter_group.main: Refreshing state... [id=web-team-3-dev-mysql8-params]
aws_dynamodb_table.terraform_lock: Refreshing state... [id=web-team-3-tf-lock-v2]
aws_s3_bucket.terraform_state: Refreshing state... [id=web-team-3-tf-state-v2]
data.aws_ami.ubuntu_2404: Read complete after 1s [id=ami-0130d8d35bcd2d433]
aws_route_table.private: Refreshing state... [id=rtb-0355eb582b4ff3dd2]
aws_subnet.public[1]: Refreshing state... [id=subnet-07e38fbb8cfadc882]
aws_subnet.private[1]: Refreshing state... [id=subnet-02884b8d343ff25a5]
aws_internet_gateway.main: Refreshing state... [id=igw-0e141727ecff126ee]
aws_subnet.private[0]: Refreshing state... [id=subnet-007a290b1fdb51067]
aws_subnet.public[0]: Refreshing state... [id=subnet-0efa7ca9b8d1e451d]
aws_security_group.ec2: Refreshing state... [id=sg-0b5c350188ea252e5]
aws_s3_bucket_public_access_block.terraform_state: Refreshing state... [id=web-team-3-tf-state-v2]
aws_s3_bucket_versioning.terraform_state: Refreshing state... [id=web-team-3-tf-state-v2]
aws_s3_bucket_server_side_encryption_configuration.terraform_state: Refreshing state... [id=web-team-3-tf-state-v2]
aws_route_table.public: Refreshing state... [id=rtb-0dad63dad02ddc5ff]
aws_route_table_association.private[1]: Refreshing state... [id=rtbassoc-0d27850a8a6e94ba5]
aws_db_subnet_group.main: Refreshing state... [id=web-team-3-dev-db-subnet-group]
aws_route_table_association.private[0]: Refreshing state... [id=rtbassoc-06218dad0b08de21c]
aws_security_group.rds: Refreshing state... [id=sg-03705313b48995086]
aws_instance.app: Refreshing state... [id=i-09f54da883306a39e]
aws_route_table_association.public[0]: Refreshing state... [id=rtbassoc-099953f254a4d5809]
aws_route_table_association.public[1]: Refreshing state... [id=rtbassoc-03c01253dea21ea7b]
aws_db_instance.main: Refreshing state... [id=db-CDWN44WOQ6PTQFDBPY6KNBS2ME]
aws_eip.app: Refreshing state... [id=eipalloc-0b8749a716be29355]

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration
and found no differences, so no changes are needed.

Pushed by: @catturtle123, Action: pull_request

@coderabbitai
Copy link

coderabbitai bot commented Jan 30, 2026

Warning

Rate limit exceeded

@catturtle123 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 8 minutes and 31 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📝 Walkthrough

Walkthrough

Adds a member withdrawal API (handler, service, DTO, tests) and OpenAPI docs; makes member-related DB foreign keys nullable (member_id -> Option and on_delete -> SetNull); updates retrospect flows to handle optional member_id; adds AppError::MemberNotFound.

Changes

Cohort / File(s) Summary
Database Entity Updates
codes/server/src/domain/member/entity/member_response.rs, codes/server/src/domain/member/entity/member_retro.rs, codes/server/src/domain/member/entity/member_retro_room.rs
Made member_id fields nullable (i64Option<i64>); changed relation on_delete from Cascade to SetNull.
Member Domain Additions
codes/server/src/domain/member/dto.rs, codes/server/src/domain/member/handler.rs, codes/server/src/domain/member/service.rs, codes/server/src/domain/member/mod.rs
Added SuccessWithdrawResponse DTO with From<BaseResponse<()>>; new withdraw handler with OpenAPI annotations; implemented MemberService::withdraw transactional deletion logic; exported dto, handler, service.
Retrospect Service Adjustments
codes/server/src/domain/retrospect/service.rs
Updated inserts to use Some(member_id) and made retrieval/mapping flows option-aware (use filter_map, handle None member_id, render withdrawn members as 탈퇴한 멤버).
App Wiring & Docs
codes/server/src/main.rs
Registered DELETE /api/v1/members/me route; added withdraw path, SuccessWithdrawResponse schema, and Member tag to OpenAPI.
Error Handling
codes/server/src/utils/error.rs
Added AppError::MemberNotFound(String) and mapped its message, error code MEMBER4042, and HTTP 404 status.
Tests
codes/server/tests/member_withdraw_test.rs
Added tests for withdrawal endpoint covering success (200 COMMON200), missing/invalid auth (401 AUTH4001), and user-not-found (404 MEMBER4042).

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant Handler as WithdrawHandler
    participant Service as MemberService
    participant DB as Database
    participant Responder as Responder

    Client->>Handler: DELETE /api/v1/members/me (Authorization: Bearer <token>)
    Handler->>Handler: extract member_id from token
    alt token invalid / parse fails
        Handler->>Responder: 401 AUTH4001
    else token parsed
        Handler->>Service: withdraw(state, member_id)
        Service->>DB: BEGIN TRANSACTION
        Service->>DB: SELECT member WHERE id = ?
        alt no member found
            Service->>Responder: 404 MEMBER4042
            Service->>DB: ROLLBACK
        else member exists
            Service->>DB: DELETE FROM refresh_tokens WHERE member_id = ?
            Service->>DB: DELETE FROM members WHERE id = ?
            Note over DB: FK-related fields set to NULL (ON DELETE SET NULL)
            Service->>DB: COMMIT
            Service->>Responder: 200 COMMON200 (SuccessWithdrawResponse)
        end
    end
    Responder->>Client: JSON response
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐇 I hop away, the tokens cleared,
FK fields nulled, no ties adhered,
A DELETE, a commit, tests nod true,
Docs updated, farewell to you,
Small carrot celebration — chew!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main objective: implementing a member withdrawal API (회원 탈퇴 API 구현) with a specific ticket reference (API-028), directly matching the PR's primary purpose and changes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/member-withdrawal

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

Terraform Format 🖌 failure

Terraform Init ⚙️ success

Terraform Validate 🤖 success

Terraform Plan 📖 success

Show Plan
Acquiring state lock. This may take a few moments...
data.aws_ami.ubuntu_2404: Reading...
aws_dynamodb_table.terraform_lock: Refreshing state... [id=web-team-3-tf-lock-v2]
aws_s3_bucket.terraform_state: Refreshing state... [id=web-team-3-tf-state-v2]
aws_db_parameter_group.main: Refreshing state... [id=web-team-3-dev-mysql8-params]
aws_vpc.main: Refreshing state... [id=vpc-06e337fe8c72d274b]
data.aws_ami.ubuntu_2404: Read complete after 1s [id=ami-0130d8d35bcd2d433]
aws_route_table.private: Refreshing state... [id=rtb-0355eb582b4ff3dd2]
aws_internet_gateway.main: Refreshing state... [id=igw-0e141727ecff126ee]
aws_subnet.public[1]: Refreshing state... [id=subnet-07e38fbb8cfadc882]
aws_subnet.private[0]: Refreshing state... [id=subnet-007a290b1fdb51067]
aws_subnet.private[1]: Refreshing state... [id=subnet-02884b8d343ff25a5]
aws_subnet.public[0]: Refreshing state... [id=subnet-0efa7ca9b8d1e451d]
aws_security_group.ec2: Refreshing state... [id=sg-0b5c350188ea252e5]
aws_route_table.public: Refreshing state... [id=rtb-0dad63dad02ddc5ff]
aws_db_subnet_group.main: Refreshing state... [id=web-team-3-dev-db-subnet-group]
aws_route_table_association.private[0]: Refreshing state... [id=rtbassoc-06218dad0b08de21c]
aws_route_table_association.private[1]: Refreshing state... [id=rtbassoc-0d27850a8a6e94ba5]
aws_security_group.rds: Refreshing state... [id=sg-03705313b48995086]
aws_instance.app: Refreshing state... [id=i-09f54da883306a39e]
aws_route_table_association.public[0]: Refreshing state... [id=rtbassoc-099953f254a4d5809]
aws_route_table_association.public[1]: Refreshing state... [id=rtbassoc-03c01253dea21ea7b]
aws_s3_bucket_server_side_encryption_configuration.terraform_state: Refreshing state... [id=web-team-3-tf-state-v2]
aws_s3_bucket_public_access_block.terraform_state: Refreshing state... [id=web-team-3-tf-state-v2]
aws_s3_bucket_versioning.terraform_state: Refreshing state... [id=web-team-3-tf-state-v2]
aws_db_instance.main: Refreshing state... [id=db-CDWN44WOQ6PTQFDBPY6KNBS2ME]
aws_eip.app: Refreshing state... [id=eipalloc-0b8749a716be29355]

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration
and found no differences, so no changes are needed.

Pushed by: @catturtle123, Action: pull_request

@github-actions
Copy link

Terraform Format 🖌 failure

Terraform Init ⚙️ success

Terraform Validate 🤖 success

Terraform Plan 📖 success

Show Plan
Acquiring state lock. This may take a few moments...
aws_vpc.main: Refreshing state... [id=vpc-06e337fe8c72d274b]
data.aws_ami.ubuntu_2404: Reading...
aws_db_parameter_group.main: Refreshing state... [id=web-team-3-dev-mysql8-params]
aws_s3_bucket.terraform_state: Refreshing state... [id=web-team-3-tf-state-v2]
aws_dynamodb_table.terraform_lock: Refreshing state... [id=web-team-3-tf-lock-v2]
data.aws_ami.ubuntu_2404: Read complete after 1s [id=ami-0130d8d35bcd2d433]
aws_internet_gateway.main: Refreshing state... [id=igw-0e141727ecff126ee]
aws_subnet.public[1]: Refreshing state... [id=subnet-07e38fbb8cfadc882]
aws_subnet.private[1]: Refreshing state... [id=subnet-02884b8d343ff25a5]
aws_route_table.private: Refreshing state... [id=rtb-0355eb582b4ff3dd2]
aws_subnet.private[0]: Refreshing state... [id=subnet-007a290b1fdb51067]
aws_subnet.public[0]: Refreshing state... [id=subnet-0efa7ca9b8d1e451d]
aws_security_group.ec2: Refreshing state... [id=sg-0b5c350188ea252e5]
aws_route_table.public: Refreshing state... [id=rtb-0dad63dad02ddc5ff]
aws_route_table_association.private[1]: Refreshing state... [id=rtbassoc-0d27850a8a6e94ba5]
aws_route_table_association.private[0]: Refreshing state... [id=rtbassoc-06218dad0b08de21c]
aws_db_subnet_group.main: Refreshing state... [id=web-team-3-dev-db-subnet-group]
aws_security_group.rds: Refreshing state... [id=sg-03705313b48995086]
aws_route_table_association.public[0]: Refreshing state... [id=rtbassoc-099953f254a4d5809]
aws_route_table_association.public[1]: Refreshing state... [id=rtbassoc-03c01253dea21ea7b]
aws_instance.app: Refreshing state... [id=i-09f54da883306a39e]
aws_s3_bucket_public_access_block.terraform_state: Refreshing state... [id=web-team-3-tf-state-v2]
aws_s3_bucket_versioning.terraform_state: Refreshing state... [id=web-team-3-tf-state-v2]
aws_s3_bucket_server_side_encryption_configuration.terraform_state: Refreshing state... [id=web-team-3-tf-state-v2]
aws_db_instance.main: Refreshing state... [id=db-CDWN44WOQ6PTQFDBPY6KNBS2ME]
aws_eip.app: Refreshing state... [id=eipalloc-0b8749a716be29355]

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration
and found no differences, so no changes are needed.

Pushed by: @catturtle123, Action: pull_request

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
codes/server/src/domain/retrospect/service.rs (1)

2467-2491: ⚠️ Potential issue | 🟠 Major

Question ordering breaks when all member_id values are NULL.

If all member_response rows have NULL member_id, member_response_map becomes empty and question_texts is empty, so category-specific queries return nothing even though responses exist. Add a fallback to derive question order from all_responses.

🛠️ Suggested fix
         // member_id별로 그룹화하여 첫 번째 멤버의 응답 세트 확인
         let mut member_response_map: HashMap<i64, Vec<i64>> = HashMap::new();
         for mr in &first_member_responses {
             if let Some(member_id) = mr.member_id {
                 member_response_map
                     .entry(member_id)
                     .or_default()
                     .push(mr.response_id);
             }
         }
 
-        // 첫 번째 멤버의 응답 ID 목록 (오름차순 정렬됨)
-        let first_member_id = member_response_map.keys().min().copied();
-        let question_response_ids: Vec<i64> = first_member_id
-            .and_then(|mid| member_response_map.get(&mid))
-            .cloned()
-            .unwrap_or_default();
-
-        // 질문 텍스트 순서를 response_id 순으로 매핑
-        let response_map: HashMap<i64, &response::Model> =
-            all_responses.iter().map(|r| (r.response_id, r)).collect();
-
-        let question_texts: Vec<String> = question_response_ids
-            .iter()
-            .filter_map(|rid| response_map.get(rid).map(|r| r.question.clone()))
-            .collect();
+        // 질문 텍스트 순서를 response_id 순으로 매핑
+        let response_map: HashMap<i64, &response::Model> =
+            all_responses.iter().map(|r| (r.response_id, r)).collect();
+
+        let question_texts: Vec<String> = if let Some(first_member_id) =
+            member_response_map.keys().min().copied()
+        {
+            member_response_map
+                .get(&first_member_id)
+                .into_iter()
+                .flatten()
+                .filter_map(|rid| response_map.get(rid).map(|r| r.question.clone()))
+                .collect()
+        } else {
+            let mut seen = HashSet::new();
+            all_responses
+                .iter()
+                .filter(|r| seen.insert(r.question.clone()))
+                .map(|r| r.question.clone())
+                .collect()
+        };
🤖 Fix all issues with AI agents
In `@codes/server/src/domain/retrospect/service.rs`:
- Around line 1931-1934: participant_names is built using filter_map which drops
entries with member_id == None, causing withdrawn members to be omitted; change
the logic in the participant_names construction to map each member_retros entry
to a String: if mr.member_id is Some(id) then lookup
member_map.get(&id).cloned().unwrap_or("탈퇴한 멤버".into()) else use "탈퇴한
멤버".into(), so every member_retros produces a name (either the found name or the
placeholder) instead of being filtered out.
- Around line 2255-2258: The code collects member_ids from submitted_members
into member_ids and later builds members_data then calls analyze_retrospective;
add a guard before the analyze_retrospective invocation to check that
members_data (or member_ids) is not empty and return a data-insufficient error
(e.g., Err(...) or a DomainError variant) instead of calling
analyze_retrospective with empty input. Locate the block that computes
member_ids and the subsequent call to analyze_retrospective and insert a
validation that returns early with a clear data-insufficient error when
member_ids.is_empty() (or members_data.is_empty()) to prevent running AI
analysis on empty data.

@github-actions
Copy link

Terraform Format 🖌 failure

Terraform Init ⚙️ success

Terraform Validate 🤖 success

Terraform Plan 📖 success

Show Plan
Acquiring state lock. This may take a few moments...
data.aws_ami.ubuntu_2404: Reading...
aws_vpc.main: Refreshing state... [id=vpc-06e337fe8c72d274b]
aws_db_parameter_group.main: Refreshing state... [id=web-team-3-dev-mysql8-params]
aws_dynamodb_table.terraform_lock: Refreshing state... [id=web-team-3-tf-lock-v2]
aws_s3_bucket.terraform_state: Refreshing state... [id=web-team-3-tf-state-v2]
data.aws_ami.ubuntu_2404: Read complete after 1s [id=ami-0130d8d35bcd2d433]
aws_internet_gateway.main: Refreshing state... [id=igw-0e141727ecff126ee]
aws_subnet.private[0]: Refreshing state... [id=subnet-007a290b1fdb51067]
aws_subnet.private[1]: Refreshing state... [id=subnet-02884b8d343ff25a5]
aws_subnet.public[1]: Refreshing state... [id=subnet-07e38fbb8cfadc882]
aws_route_table.private: Refreshing state... [id=rtb-0355eb582b4ff3dd2]
aws_subnet.public[0]: Refreshing state... [id=subnet-0efa7ca9b8d1e451d]
aws_security_group.ec2: Refreshing state... [id=sg-0b5c350188ea252e5]
aws_db_subnet_group.main: Refreshing state... [id=web-team-3-dev-db-subnet-group]
aws_s3_bucket_public_access_block.terraform_state: Refreshing state... [id=web-team-3-tf-state-v2]
aws_s3_bucket_versioning.terraform_state: Refreshing state... [id=web-team-3-tf-state-v2]
aws_s3_bucket_server_side_encryption_configuration.terraform_state: Refreshing state... [id=web-team-3-tf-state-v2]
aws_route_table_association.private[1]: Refreshing state... [id=rtbassoc-0d27850a8a6e94ba5]
aws_route_table_association.private[0]: Refreshing state... [id=rtbassoc-06218dad0b08de21c]
aws_route_table.public: Refreshing state... [id=rtb-0dad63dad02ddc5ff]
aws_security_group.rds: Refreshing state... [id=sg-03705313b48995086]
aws_instance.app: Refreshing state... [id=i-09f54da883306a39e]
aws_route_table_association.public[0]: Refreshing state... [id=rtbassoc-099953f254a4d5809]
aws_route_table_association.public[1]: Refreshing state... [id=rtbassoc-03c01253dea21ea7b]
aws_db_instance.main: Refreshing state... [id=db-CDWN44WOQ6PTQFDBPY6KNBS2ME]
aws_eip.app: Refreshing state... [id=eipalloc-0b8749a716be29355]

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration
and found no differences, so no changes are needed.

Pushed by: @catturtle123, Action: pull_request

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
codes/server/src/domain/retrospect/service.rs (1)

2482-2505: ⚠️ Potential issue | 🟠 Major

Fix: category filtering breaks when all member_id are NULL.
If all member_id values are None (withdrawn members), member_response_map is empty, question_texts becomes empty, and category queries return empty despite existing responses. Add a fallback that derives question order from all_responses when the map is empty.

🛠️ Suggested fix (fallback when no member-response mapping)
-        // 질문 텍스트 순서를 response_id 순으로 매핑
-        let response_map: HashMap<i64, &response::Model> =
-            all_responses.iter().map(|r| (r.response_id, r)).collect();
-
-        let question_texts: Vec<String> = question_response_ids
-            .iter()
-            .filter_map(|rid| response_map.get(rid).map(|r| r.question.clone()))
-            .collect();
+        // 질문 텍스트 순서를 response_id 순으로 매핑
+        let response_map: HashMap<i64, &response::Model> =
+            all_responses.iter().map(|r| (r.response_id, r)).collect();
+
+        let question_texts: Vec<String> = if !question_response_ids.is_empty() {
+            question_response_ids
+                .iter()
+                .filter_map(|rid| response_map.get(rid).map(|r| r.question.clone()))
+                .collect()
+        } else {
+            let mut seen_questions = HashSet::new();
+            all_responses
+                .iter()
+                .filter(|r| seen_questions.insert(r.question.clone()))
+                .take(QUESTIONS_PER_RETROSPECT)
+                .map(|r| r.question.clone())
+                .collect()
+        };
🧹 Nitpick comments (1)
codes/server/src/domain/retrospect/service.rs (1)

1372-1411: Consider whether withdrawn members should appear in detail responses.
filter_map drops None and missing member rows, so participants can disappear from the API response. If the UI needs visibility of withdrawn members, consider a placeholder name and making RetrospectMemberItem.member_id optional.

@github-actions
Copy link

Terraform Format 🖌 failure

Terraform Init ⚙️ success

Terraform Validate 🤖 success

Terraform Plan 📖 success

Show Plan
Acquiring state lock. This may take a few moments...
data.aws_ami.ubuntu_2404: Reading...
aws_db_parameter_group.main: Refreshing state... [id=web-team-3-dev-mysql8-params]
aws_dynamodb_table.terraform_lock: Refreshing state... [id=web-team-3-tf-lock-v2]
aws_vpc.main: Refreshing state... [id=vpc-06e337fe8c72d274b]
aws_s3_bucket.terraform_state: Refreshing state... [id=web-team-3-tf-state-v2]
data.aws_ami.ubuntu_2404: Read complete after 1s [id=ami-0130d8d35bcd2d433]
aws_internet_gateway.main: Refreshing state... [id=igw-0e141727ecff126ee]
aws_subnet.public[0]: Refreshing state... [id=subnet-0efa7ca9b8d1e451d]
aws_subnet.public[1]: Refreshing state... [id=subnet-07e38fbb8cfadc882]
aws_subnet.private[0]: Refreshing state... [id=subnet-007a290b1fdb51067]
aws_subnet.private[1]: Refreshing state... [id=subnet-02884b8d343ff25a5]
aws_route_table.private: Refreshing state... [id=rtb-0355eb582b4ff3dd2]
aws_security_group.ec2: Refreshing state... [id=sg-0b5c350188ea252e5]
aws_route_table.public: Refreshing state... [id=rtb-0dad63dad02ddc5ff]
aws_s3_bucket_public_access_block.terraform_state: Refreshing state... [id=web-team-3-tf-state-v2]
aws_s3_bucket_versioning.terraform_state: Refreshing state... [id=web-team-3-tf-state-v2]
aws_s3_bucket_server_side_encryption_configuration.terraform_state: Refreshing state... [id=web-team-3-tf-state-v2]
aws_security_group.rds: Refreshing state... [id=sg-03705313b48995086]
aws_route_table_association.public[1]: Refreshing state... [id=rtbassoc-03c01253dea21ea7b]
aws_route_table_association.private[0]: Refreshing state... [id=rtbassoc-06218dad0b08de21c]
aws_route_table_association.public[0]: Refreshing state... [id=rtbassoc-099953f254a4d5809]
aws_db_subnet_group.main: Refreshing state... [id=web-team-3-dev-db-subnet-group]
aws_route_table_association.private[1]: Refreshing state... [id=rtbassoc-0d27850a8a6e94ba5]
aws_instance.app: Refreshing state... [id=i-09f54da883306a39e]
aws_db_instance.main: Refreshing state... [id=db-CDWN44WOQ6PTQFDBPY6KNBS2ME]
aws_eip.app: Refreshing state... [id=eipalloc-0b8749a716be29355]

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration
and found no differences, so no changes are needed.

Pushed by: @catturtle123, Action: pull_request

@catturtle123 catturtle123 merged commit 9bf6c2b into dev Jan 30, 2026
4 checks passed
@catturtle123 catturtle123 deleted the feat/member-withdrawal branch January 30, 2026 15:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants