Implement community groups and events with RSVP functionality.
Created Group.cs with:
- Tenant isolation (ITenantEntity)
- Name, Description, IsPrivate, ImageUrl
- CreatedById (owner reference)
- Member collection navigation
Created GroupMember.cs with:
- Group/User junction
- Role: member, admin, owner
- JoinedAt timestamp
Created Event.cs with:
- Tenant isolation
- Title, Description, Location
- StartsAt, EndsAt, MaxAttendees
- Optional GroupId for group events
- IsCancelled flag
- RSVP status constants
Created EventRsvp.cs with:
- Event/User junction
- Status: going, maybe, not_going
- RespondedAt timestamp
Added to NexusDbContext:
- groups, group_members, events, event_rsvps tables
- Global query filters for tenant isolation
- Unique constraints (one membership per user/group, one RSVP per user/event)
- Cascade deletes for related data
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/groups | List all groups (paginated, searchable) |
| GET | /api/groups/my | List groups I'm a member of |
| GET | /api/groups/{id} | Get single group with my membership |
| POST | /api/groups | Create a group (auto become owner) |
| PUT | /api/groups/{id} | Update group (admin/owner only) |
| DELETE | /api/groups/{id} | Delete group (owner only) |
| GET | /api/groups/{id}/members | List group members |
| POST | /api/groups/{id}/join | Join a public group |
| DELETE | /api/groups/{id}/leave | Leave a group |
| POST | /api/groups/{id}/members | Add member (admin/owner only) |
| DELETE | /api/groups/{id}/members/{memberId} | Remove member |
| PUT | /api/groups/{id}/members/{memberId}/role | Update member role |
| PUT | /api/groups/{id}/transfer-ownership | Transfer ownership |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/events | List events (paginated, filterable) |
| GET | /api/events/my | List events I've RSVP'd to |
| GET | /api/events/{id} | Get single event with my RSVP |
| POST | /api/events | Create an event (auto RSVP as going) |
| PUT | /api/events/{id} | Update event (creator/admin only) |
| PUT | /api/events/{id}/cancel | Cancel event |
| DELETE | /api/events/{id} | Delete event |
| GET | /api/events/{id}/rsvps | List event RSVPs |
| POST | /api/events/{id}/rsvp | RSVP to event |
| DELETE | /api/events/{id}/rsvp | Remove RSVP |
# Start the Docker stack (migrations run automatically)
docker compose up -d
# Or run migrations manually inside container
docker compose exec api dotnet ef database updateSave and run as PowerShell script after API is running:
# See test-groups-events.ps1 in scratchpadsrc/Nexus.Api/Entities/Group.cssrc/Nexus.Api/Entities/GroupMember.cssrc/Nexus.Api/Entities/Event.cssrc/Nexus.Api/Entities/EventRsvp.cssrc/Nexus.Api/Controllers/GroupsController.cssrc/Nexus.Api/Controllers/EventsController.cs
src/Nexus.Api/Data/NexusDbContext.cs- Added 4 new DbSets and configurations
- Creator automatically becomes owner
- Private groups require admin to add members
- Owner cannot leave (must transfer or delete)
- Only owner can change roles and transfer ownership
- Admins can add/remove members but not other admins
- Creator automatically RSVPs as going
- Group events require group membership to create
- Can filter by group, upcoming only, or search
- MaxAttendees enforced on "going" RSVPs
- Cancelled events cannot receive new RSVPs
=== Phase 11: Groups & Events Tests ===
1. Register User A...
User A registered (ID: 67)
2. Register User B...
User B registered (ID: 68)
=== Groups Tests ===
3. User A creates a public group...
Group created (ID: 3)
Name: Test Community Group
My Role: owner
4. List all groups...
Found 1 group(s)
5. Get group details...
Group: Test Community Group
Members: 1
My Role: owner
6. User B joins the group...
Joined group successfully
Role: member
7. List group members...
Found 2 member(s)
- GroupA Test (owner)
- GroupB Test (member)
8. Owner promotes User B to admin...
Member role updated
9. Verify role change...
User B's role: admin
10. Admin updates group description...
Group updated
11. User A creates a private group...
Private group created (ID: 4)
12. User B tries to join private group (should fail)...
Correctly rejected: Private group requires admin
=== Events Tests ===
13. User A creates a community event...
Event created (ID: 2)
Title: Community Meetup
My RSVP: going
14. List all events...
Found 2 event(s)
15. Get event details...
Event: Community Meetup
Location: Town Hall
Going: 1
16. User B RSVPs as going...
RSVP recorded
17. Check RSVP counts...
Going: 2
18. User B changes RSVP to maybe...
RSVP recorded
19. User B lists their events...
Found 1 event(s) I've RSVP'd to
20. Get RSVPs for event...
Found 2 RSVP(s)
- GroupA Test: going
- GroupB Test: maybe
21. User A creates a group event...
Group event created (ID: 3)
22. Filter events by group...
Found 1 event(s) for this group
23. Cancel community event...
Event cancelled
24. User B removes RSVP...
RSVP removed
25. User B leaves the group...
Left group successfully
26. Test tenant isolation...
Globex sees 0 group(s) - Tenant isolation working!
Globex sees 0 event(s) - Tenant isolation working!
27. Cleanup - delete groups...
Groups deleted
=== All Phase 11 Tests PASSED ===