Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

---

## [0.4.0] - 2026-08-22

### Added
- Team workspace management module with workspace creation, invite code generation, invite-based joining, and role governance (`OWNER`, `ADMIN`, `MEMBER`).
- Team REST endpoints (`/api/v1/teams`, `/api/v1/teams/{id}`, `/api/v1/teams/join`, `/api/v1/teams/{id}/invite-code/regenerate`, `/api/v1/teams/{id}/members`, `/api/v1/teams/{id}/members/{userId}/role`).
- Threaded hierarchical task comments with recursive replies, author editing, and soft deletion (`/api/v1/tasks/{taskId}/comments`, `/api/v1/comments/{id}`).
- Task file attachments module supporting multipart uploads (up to 10MB), S3/MinIO compatible storage, and pre-signed download URLs (`/api/v1/tasks/{taskId}/attachments`, `/api/v1/attachments/{id}`).
- Database migrations:
- `V6__create_teams_table.sql`: `teams` workspace table with unique invite codes and owner constraints.
- `V7__create_team_members_table.sql`: `team_members` membership table with role check constraints.
- `V8__add_team_fk_to_tasks.sql`: Foreign key constraint linking tasks to team workspaces.
- `V9__create_task_comments_table.sql`: `task_comments` table with hierarchical parent-child relationships and soft deletion.
- `V10__create_task_attachments_table.sql`: `task_attachments` table tracking file metadata and object storage keys.
- Domain event publishing for team and collaboration lifecycles (`TeamCreatedEvent`, `TeamMemberJoinedEvent`, `TeamMemberRoleUpdatedEvent`, `TeamMemberRemovedEvent`, `TeamDeletedEvent`, `TaskCommentCreatedEvent`, `TaskCommentUpdatedEvent`, `TaskCommentDeletedEvent`, `TaskAttachmentUploadedEvent`, `TaskAttachmentDeletedEvent`).
- Comprehensive unit, MockMvc, and end-to-end integration test suites across team workspaces and collaboration flows (73 total tests passing).
- Production-grade REST API specification document in `docs/api-specification.md`.

---

## [0.3.0] - 2026-08-21

### Added
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ TaskMaster exposes a versioned, RESTful API secured by asymmetric RS256 JWTs wit
| **Authentication** | `/api/v1/auth` | User registration, login, refresh token rotation, logout, and public JWKS keys | No (Rate Limited) |
| **User Profile** | `/api/v1/users` | Profile retrieval and display name/avatar updates | Yes (Bearer JWT) |
| **Task Management** | `/api/v1/tasks` | CRUD, lifecycle state transitions, assignee assignment, JPA criteria filtering, full-text search, and soft deletion | Yes (Bearer JWT) |
| **Team Workspaces** | `/api/v1/teams` | Workspace creation, member governance, secure invite code generation/joining, and role management | Yes (Bearer JWT) |
| **Task Comments** | `/api/v1/tasks/{taskId}/comments` | Hierarchical threaded task comments, nested replies, editing, and soft deletion | Yes (Bearer JWT) |
| **Task Attachments** | `/api/v1/tasks/{taskId}/attachments` | Multipart file upload (Max 10MB), S3/MinIO storage, pre-signed download URLs, and deletion | Yes (Bearer JWT) |

---

Expand All @@ -114,12 +117,13 @@ Track implementation progress across release phases:
- ✅ **Phase 1: Foundation & Project Setup**
- ✅ **Phase 2: User Authentication & Authorization**
- ✅ **Phase 3: Task Management**
- ⬜ **Phase 4: Team Collaboration**
- ✅ **Phase 4: Team Collaboration**
- ⬜ **Phase 5: Real-time Notifications & AI Integration**
- ⬜ **Phase 6: Advanced Search & Analytics**
- ⬜ **Phase 7: Performance & Reliability**
- ⬜ **Phase 8: DevOps & Cloud Deployment**
- ⬜ **Phase 9: Extended Platform Capabilities**
- ⬜ **Phase 10: Modern Collaborative Web Application (Frontend)**

For detailed milestone breakdowns, see [docs/ROADMAP.md](./docs/ROADMAP.md).

Expand Down
13 changes: 12 additions & 1 deletion docs/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ A trackable overview of delivery milestones and planned capabilities for the Tas
- PostgreSQL full-text search (`tsvector` & GIN index)
- Domain event publishing for lifecycle triggers

- ⬜ **Phase 4: Team Collaboration**
- ✅ **Phase 4: Team Collaboration**
- Team & project workspace creation with role-based member governance
- Secure invite code generation and onboarding workflow
- Team-scoped task querying and authorization gates
Expand Down Expand Up @@ -70,9 +70,20 @@ A trackable overview of delivery milestones and planned capabilities for the Tas
- Kanban board drag-and-drop positional reordering
- Webhooks dispatching system for external automation

- ⬜ **Phase 10: Modern Collaborative Web Application (Frontend)**
- Next.js (App Router) & React 19 single-page progressive web application
- Responsive, accessible UI system using Tailwind CSS, Radix UI primitives, and Lucide icons
- Interactive Kanban board with drag-and-drop state transitions (`@dnd-kit`)
- Real-time notification center and live task update subscriptions via WebSocket/STOMP
- Rich Markdown task editor with live preview and syntax highlighting
- Multi-workspace switcher, invite code modals, and team member management dashboard
- Embedded AI assistant panel for natural language task drafting and comment summarization
- Client state & server synchronization via TanStack Query and Zustand

---

### Legend
- ✅ Complete
- 🔄 In Progress
- ⬜ Planned

170 changes: 169 additions & 1 deletion docs/api-specification.md
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,174 @@ All non-2xx responses adhere to the **RFC 7807 `ProblemDetail`** standard:
"data": {
"message": "Task successfully deleted"
},
"timestamp": "2026-08-21T10:00:00Z"
"timestamp": "2026-08-22T10:00:00Z"
}
```

---

### 4. Team Workspace Governance (`/api/v1/teams`)

#### 4.1 Create Team Workspace
- **Method**: `POST /api/v1/teams`
- **Auth**: `Bearer <token>`
- **Request Body**:
```json
{
"name": "Platform Engineering",
"description": "Core infrastructure, foundations, and developer tooling"
}
```
- **Response**: `201 Created`
```json
{
"success": true,
"data": {
"id": "e5f6a7b8-9012-cdef-3456-789012345678",
"name": "Platform Engineering",
"description": "Core infrastructure, foundations, and developer tooling",
"owner": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"username": "alexdev",
"displayName": "Alex Developer"
},
"inviteCode": "K8X9MZ2P4W",
"memberCount": 1,
"createdAt": "2026-08-22T10:00:00Z",
"updatedAt": "2026-08-22T10:00:00Z"
},
"timestamp": "2026-08-22T10:00:00Z"
}
```

#### 4.2 Get Team Details & Members
- **Method**: `GET /api/v1/teams/{id}`
- **Auth**: `Bearer <token>` (Team member only)
- **Response**: `200 OK` (`TeamDetailResponse`)

#### 4.3 List My Team Workspaces
- **Method**: `GET /api/v1/teams`
- **Auth**: `Bearer <token>`
- **Response**: `200 OK` (`List<TeamResponse>`)

#### 4.4 Join Team with Invite Code
- **Method**: `POST /api/v1/teams/join`
- **Auth**: `Bearer <token>`
- **Request Body**:
```json
{
"inviteCode": "K8X9MZ2P4W"
}
```
- **Response**: `200 OK` (`TeamResponse`)

#### 4.5 Regenerate Team Invite Code
- **Method**: `POST /api/v1/teams/{id}/invite-code/regenerate`
- **Auth**: `Bearer <token>` (`OWNER` or `ADMIN` only)
- **Response**: `200 OK` (`TeamResponse`)

#### 4.6 List Team Members
- **Method**: `GET /api/v1/teams/{id}/members`
- **Auth**: `Bearer <token>` (Team member only)
- **Response**: `200 OK` (`List<TeamMemberResponse>`)

#### 4.7 Update Member Role
- **Method**: `PATCH /api/v1/teams/{id}/members/{userId}/role`
- **Auth**: `Bearer <token>` (`OWNER` only)
- **Request Body**:
```json
{
"role": "ADMIN"
}
```
- **Response**: `200 OK` (`TeamMemberResponse`)

#### 4.8 Remove Member / Leave Team
- **Method**: `DELETE /api/v1/teams/{id}/members/{userId}`
- **Auth**: `Bearer <token>` (`OWNER`, `ADMIN`, or self-leave)
- **Response**: `200 OK`

#### 4.9 Delete Team Workspace
- **Method**: `DELETE /api/v1/teams/{id}`
- **Auth**: `Bearer <token>` (`OWNER` only)
- **Response**: `200 OK`

---

### 5. Threaded Comments & Discussions

#### 5.1 Post Comment or Reply to Task
- **Method**: `POST /api/v1/tasks/{taskId}/comments`
- **Auth**: `Bearer <token>`
- **Request Body**:
```json
{
"content": "RFC architecture review passed. Ready for deployment.",
"parentCommentId": "d4e5f6a7-b890-12cd-ef34-567890123456"
}
```
*(Leave `parentCommentId: null` for root-level discussion threads)*
- **Response**: `201 Created` (`CommentResponse`)

#### 5.2 Get Threaded Comments
- **Method**: `GET /api/v1/tasks/{taskId}/comments`
- **Auth**: `Bearer <token>`
- **Response**: `200 OK` (`List<CommentResponse>` with nested `replies` tree)

#### 5.3 Edit Comment
- **Method**: `PUT /api/v1/comments/{id}`
- **Auth**: `Bearer <token>` (Author only)
- **Request Body**:
```json
{
"content": "Updated comment content"
}
```
- **Response**: `200 OK` (`CommentResponse`)

#### 5.4 Soft Delete Comment
- **Method**: `DELETE /api/v1/comments/{id}`
- **Auth**: `Bearer <token>` (Author only)
- **Response**: `200 OK`

---

### 6. Task Attachments & Object Storage

#### 6.1 Upload File Attachment
- **Method**: `POST /api/v1/tasks/{taskId}/attachments`
- **Content-Type**: `multipart/form-data`
- **Auth**: `Bearer <token>`
- **Form Data**: `file` (Multipart file, max 10MB)
- **Response**: `201 Created`
```json
{
"success": true,
"data": {
"id": "f6a7b890-12cd-ef34-5678-901234567890",
"taskId": "c3d4e5f6-a7b8-9012-cdef-345678901234",
"uploader": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"username": "alexdev",
"displayName": "Alex Developer"
},
"fileName": "system-architecture.pdf",
"fileSize": 245890,
"contentType": "application/pdf",
"downloadUrl": "http://localhost:9000/taskmaster-attachments/tasks/c3d4e5f6-a7b8-9012-cdef-345678901234/uuid-system-architecture.pdf?response-content-disposition=attachment;filename=\"system-architecture.pdf\"",
"createdAt": "2026-08-22T10:00:00Z"
},
"timestamp": "2026-08-22T10:00:00Z"
}
```

#### 6.2 List Task Attachments
- **Method**: `GET /api/v1/tasks/{taskId}/attachments`
- **Auth**: `Bearer <token>`
- **Response**: `200 OK` (`List<AttachmentResponse>` with generated pre-signed URLs)

#### 6.3 Delete Attachment
- **Method**: `DELETE /api/v1/attachments/{id}`
- **Auth**: `Bearer <token>` (Uploader only)
- **Response**: `200 OK`

Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.taskmaster.collaboration.adapter.in;

import com.taskmaster.collaboration.application.dto.AttachmentResponse;
import com.taskmaster.collaboration.application.service.TaskAttachmentService;
import com.taskmaster.shared.dto.ApiResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

@RestController
@Tag(name = "Attachments", description = "Task file attachments and S3/MinIO upload/download endpoints")
@SecurityRequirement(name = "BearerAuth")
public class TaskAttachmentController {

private final TaskAttachmentService attachmentService;

public TaskAttachmentController(TaskAttachmentService attachmentService) {
this.attachmentService = attachmentService;
}

@PostMapping(value = "/api/v1/tasks/{taskId}/attachments", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation(summary = "Upload a file attachment to a task (Max 10MB)")
public ResponseEntity<ApiResponse<AttachmentResponse>> uploadAttachment(
@AuthenticationPrincipal Jwt jwt,
@PathVariable("taskId") UUID taskId,
@RequestParam("file") MultipartFile file
) {
UUID uploaderId = UUID.fromString(jwt.getSubject());
AttachmentResponse response = attachmentService.uploadAttachment(taskId, uploaderId, file);
return ResponseEntity.status(HttpStatus.CREATED).body(ApiResponse.of(response));
}

@GetMapping("/api/v1/tasks/{taskId}/attachments")
@Operation(summary = "List all file attachments for a task with pre-signed download URLs")
public ResponseEntity<ApiResponse<List<AttachmentResponse>>> getTaskAttachments(
@AuthenticationPrincipal Jwt jwt,
@PathVariable("taskId") UUID taskId
) {
UUID currentUserId = UUID.fromString(jwt.getSubject());
List<AttachmentResponse> response = attachmentService.getTaskAttachments(taskId, currentUserId);
return ResponseEntity.ok(ApiResponse.of(response));
}

@DeleteMapping("/api/v1/attachments/{id}")
@Operation(summary = "Delete a file attachment (Uploader only)")
public ResponseEntity<ApiResponse<Map<String, String>>> deleteAttachment(
@AuthenticationPrincipal Jwt jwt,
@PathVariable("id") UUID id
) {
UUID currentUserId = UUID.fromString(jwt.getSubject());
attachmentService.deleteAttachment(id, currentUserId);
return ResponseEntity.ok(ApiResponse.of(Map.of("message", "Attachment successfully deleted")));
}
}
Loading
Loading