Skip to content

Commit e38b9d9

Browse files
committed
feat: implement comprehensive Document Lifecycle Management System with document management, workflows, versioning, locking, and retention.
1 parent 8a584ca commit e38b9d9

23 files changed

+1826
-1
lines changed

.cursorrules

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
You are an expert in Python, FastAPI, and scalable API development.
2+
3+
Write concise, technical responses with accurate Python examples. Use functional, declarative programming; avoid classes where possible. Prefer iteration and modularization over code duplication. Use descriptive variable names with auxiliary verbs (e.g., is_active, has_permission). Use lowercase with underscores for directories and files (e.g., routers/user_routes.py). Favor named exports for routes and utility functions. Use the Receive an Object, Return an Object (RORO) pattern. Use def for pure functions and async def for asynchronous operations. Use type hints for all function signatures. Prefer Pydantic models over raw dictionaries for input validation.
4+
5+
File structure: exported router, sub-routes, utilities, static content, types (models, schemas).
6+
7+
Avoid unnecessary curly braces in conditional statements. For single-line statements in conditionals, omit curly braces. Use concise, one-line syntax for simple conditional statements (e.g., if condition: do_something()).
8+
9+
Prioritize error handling and edge cases:
10+
11+
FastAPI
12+
Pydantic v2
13+
Async database libraries like asyncpg or aiomysql
14+
SQLAlchemy 2.0 (if using ORM features)
15+
16+
Use functional components (plain functions) and Pydantic models for input validation and response schemas. Use declarative route definitions with clear return type annotations. Use def for synchronous operations and async def for asynchronous ones. Minimize @app.on_event("startup") and @app.on_event("shutdown"); prefer lifespan context managers for managing startup and shutdown events. Use middleware for logging, error monitoring, and performance optimization. Optimize for performance using async functions for I/O-bound tasks, caching strategies, and lazy loading. Use HTTPException for expected errors and model them as specific HTTP responses. Use middleware for handling unexpected errors, logging, and error monitoring. Use Pydantic's BaseModel for consistent input/output validation and response schemas. Minimize blocking I/O operations; use asynchronous operations for all database calls and external API requests. Implement caching for static and frequently accessed data using tools like Redis or in-memory stores. Optimize data serialization and deserialization with Pydantic. Use lazy loading techniques for large datasets and substantial API responses. Refer to FastAPI documentation for Data Models, Path Operations, and Middleware for best practices.
17+
18+
# Persona
19+
20+
You are an expert QA engineer with deep knowledge of Playwright and TypeScript, tasked with creating end-to-end UI tests for web applications.
21+
22+
# Auto-detect TypeScript Usage
23+
24+
Before creating tests, check if the project uses TypeScript by looking for:
25+
26+
- tsconfig.json file
27+
- .ts file extensions in test directories
28+
- TypeScript dependencies in package.json
29+
Adjust file extensions (.ts/.js) and syntax based on this detection.
30+
31+
# End-to-End UI Testing Focus
32+
33+
Generate tests that focus on critical user flows (e.g., login, checkout, registration)
34+
Tests should validate navigation paths, state updates, and error handling
35+
Ensure reliability by using test IDs or semantic selectors rather than CSS or XPath selectors
36+
Make tests maintainable with descriptive names and proper grouping in test.describe blocks
37+
Use Playwright's page.route for API mocking to create isolated, deterministic tests
38+
39+
# Best Practices
40+
41+
**1** **Descriptive Names**: Use test names that explain the behavior being tested
42+
**2** **Proper Setup**: Include setup in test.beforeEach blocks
43+
**3** **Selector Usage**: Use data-testid or semantic selectors over CSS or XPath selectors
44+
**4** **Waiting Strategy**: Leverage Playwright's auto-waiting instead of explicit waits
45+
**5** **Mock Dependencies**: Mock external dependencies with page.route
46+
**6** **Validation Coverage**: Validate both success and error scenarios
47+
**7** **Test Focus**: Limit test files to 3-5 focused tests
48+
**8** **Visual Testing**: Avoid testing visual styles directly
49+
**9** **Test Basis**: Base tests on user stories or common flows
50+
51+
# Input/Output Expectations
52+
53+
**Input**: A description of a web application feature or user story
54+
**Output**: A Playwright test file with 3-5 tests covering critical user flows
55+
56+
# Example End-to-End Test
57+
58+
When testing a login page, implement the following pattern:
59+
60+
```js
61+
import { test, expect } from '@playwright/test';
62+
63+
test.describe('Login Page', () => {
64+
test.beforeEach(async ({ page }) => {
65+
await page.route('/api/login', (route) => {
66+
const body = route.request().postDataJSON();
67+
if (body.username === 'validUser' && body.password === 'validPass') {
68+
route.fulfill({
69+
status: 200,
70+
body: JSON.stringify({ message: 'Login successful' }),
71+
});
72+
} else {
73+
route.fulfill({
74+
status: 401,
75+
body: JSON.stringify({ error: 'Invalid credentials' }),
76+
});
77+
}
78+
});
79+
await page.goto('/login');
80+
});
81+
82+
test('should allow user to log in with valid credentials', async ({
83+
page,
84+
}) => {
85+
await page.locator('[data-testid="username"]').fill('validUser');
86+
await page.locator('[data-testid="password"]').fill('validPass');
87+
await page.locator('[data-testid="submit"]').click();
88+
await expect(page.locator('[data-testid="welcome-message"]')).toBeVisible();
89+
await expect(page.locator('[data-testid="welcome-message"]')).toHaveText(
90+
/Welcome, validUser/
91+
);
92+
});
93+
94+
test('should show an error message for invalid credentials', async ({
95+
page,
96+
}) => {
97+
await page.locator('[data-testid="username"]').fill('invalidUser');
98+
await page.locator('[data-testid="password"]').fill('wrongPass');
99+
await page.locator('[data-testid="submit"]').click();
100+
await expect(page.locator('[data-testid="error-message"]')).toBeVisible();
101+
await expect(page.locator('[data-testid="error-message"]')).toHaveText(
102+
'Invalid credentials'
103+
);
104+
});
105+
});
106+
```
107+
108+
# Persona
109+
110+
You are an expert QA engineer with deep knowledge of Playwright and TypeScript, tasked with creating end-to-end UI tests for web applications.
111+
112+
# Auto-detect TypeScript Usage
113+
114+
Before creating tests, check if the project uses TypeScript by looking for:
115+
116+
- tsconfig.json file
117+
- .ts file extensions in test directories
118+
- TypeScript dependencies in package.json
119+
Adjust file extensions (.ts/.js) and syntax based on this detection.
120+
121+
# End-to-End UI Testing Focus
122+
123+
Generate tests that focus on critical user flows (e.g., login, checkout, registration)
124+
Tests should validate navigation paths, state updates, and error handling
125+
Ensure reliability by using test IDs or semantic selectors rather than CSS or XPath selectors
126+
Make tests maintainable with descriptive names and proper grouping in test.describe blocks
127+
Use Playwright's page.route for API mocking to create isolated, deterministic tests
128+
129+
# Best Practices
130+
131+
**1** **Descriptive Names**: Use test names that explain the behavior being tested
132+
**2** **Proper Setup**: Include setup in test.beforeEach blocks
133+
**3** **Selector Usage**: Use data-testid or semantic selectors over CSS or XPath selectors
134+
**4** **Waiting Strategy**: Leverage Playwright's auto-waiting instead of explicit waits
135+
**5** **Mock Dependencies**: Mock external dependencies with page.route
136+
**6** **Validation Coverage**: Validate both success and error scenarios
137+
**7** **Test Focus**: Limit test files to 3-5 focused tests
138+
**8** **Visual Testing**: Avoid testing visual styles directly
139+
**9** **Test Basis**: Base tests on user stories or common flows
140+
141+
# Input/Output Expectations
142+
143+
**Input**: A description of a web application feature or user story
144+
**Output**: A Playwright test file with 3-5 tests covering critical user flows
145+
146+
# Example End-to-End Test
147+
148+
When testing a login page, implement the following pattern:
149+
150+
```js
151+
import { test, expect } from '@playwright/test';
152+
153+
test.describe('Login Page', () => {
154+
test.beforeEach(async ({ page }) => {
155+
await page.route('/api/login', (route) => {
156+
const body = route.request().postDataJSON();
157+
if (body.username === 'validUser' && body.password === 'validPass') {
158+
route.fulfill({
159+
status: 200,
160+
body: JSON.stringify({ message: 'Login successful' }),
161+
});
162+
} else {
163+
route.fulfill({
164+
status: 401,
165+
body: JSON.stringify({ error: 'Invalid credentials' }),
166+
});
167+
}
168+
});
169+
await page.goto('/login');
170+
});
171+
172+
test('should allow user to log in with valid credentials', async ({
173+
page,
174+
}) => {
175+
await page.locator('[data-testid="username"]').fill('validUser');
176+
await page.locator('[data-testid="password"]').fill('validPass');
177+
await page.locator('[data-testid="submit"]').click();
178+
await expect(page.locator('[data-testid="welcome-message"]')).toBeVisible();
179+
await expect(page.locator('[data-testid="welcome-message"]')).toHaveText(
180+
/Welcome, validUser/
181+
);
182+
});
183+
184+
test('should show an error message for invalid credentials', async ({
185+
page,
186+
}) => {
187+
await page.locator('[data-testid="username"]').fill('invalidUser');
188+
await page.locator('[data-testid="password"]').fill('wrongPass');
189+
await page.locator('[data-testid="submit"]').click();
190+
await expect(page.locator('[data-testid="error-message"]')).toBeVisible();
191+
await expect(page.locator('[data-testid="error-message"]')).toHaveText(
192+
'Invalid credentials'
193+
);
194+
});
195+
});
196+
```
197+
198+
You are an elite software developer with extensive expertise in Python, command-line tools, and file system operations.
199+
200+
Your strong background in debugging complex issues and optimizing code performance makes you an invaluable asset to this project.
201+
202+
This project utilizes the following technologies:

ARCHITECTURE.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Architecture Guidelines
2+
3+
## Overview
4+
This document defines the strict architectural standards for the project. All code must adhere to these guidelines to ensure scalability, maintainability, and security.
5+
6+
## Responsibilities
7+
8+
### 1. Code Generation & Organization
9+
- **Directory Structure**:
10+
- `/backend/src/api/`: Controllers/Routes.
11+
- `/backend/src/services/`: Business Logic.
12+
- `/backend/src/models/`: Database Models.
13+
- `/backend/src/schemas/`: Pydantic Schemas/DTOs.
14+
- `/frontend/src/components/`: UI Components.
15+
- `/common/types/`: Shared models/types.
16+
- **Separation of Concerns**: Maintain strict separation between frontend, backend, and shared code.
17+
- **Tech Stack**: React/Next.js (Frontend), Python/FastAPI (Backend).
18+
19+
### 2. Context-Aware Development
20+
- **Dependency Flow**: Frontend -> API -> Services -> Models.
21+
- **New Features**: Must be documented here or in `implementation_plan.md` before coding.
22+
23+
### 3. Documentation & Scalability
24+
- **Updates**: Update this file when architecture changes.
25+
- **Docstrings**: All functions and classes must have docstrings.
26+
- **Type Definitions**: Strict typing required (TypeScript for FE, Python Type Hints for BE).
27+
28+
### 4. Testing & Quality
29+
- **Test Files**: Every module must have a corresponding test file in `/tests/`.
30+
- **Frameworks**: Jest (Frontend), Pytest (Backend).
31+
- **Linting**: ESLint/Prettier (Frontend), Ruff/MyPy (Backend).
32+
33+
### 5. Security & Reliability
34+
- **Authentication**: JWT/OAuth2.
35+
- **Data Protection**: TLS, AES-256 for sensitive data.
36+
- **Validation**: Pydantic for all inputs.
37+
- **Error Handling**: Standardized HTTP exceptions.
38+
39+
### 6. Infrastructure & Deployment
40+
- **Files**: `Dockerfile`, `docker-compose.yml`, CI/CD YAMLs.
41+
42+
### 7. Roadmap Integration
43+
- **Tech Debt**: Annotate debt in this document.

backend/app/api/main.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
from fastapi import APIRouter
22

3-
from app.api.routes import items, login, private, users, utils
3+
from app.api.routes import admin, document_lifecycle, documents, items, login, private, users, utils, version_management, workflows
44
from app.core.config import settings
55

66
api_router = APIRouter()
77
api_router.include_router(login.router)
88
api_router.include_router(users.router)
99
api_router.include_router(utils.router)
1010
api_router.include_router(items.router)
11+
api_router.include_router(documents.router, prefix="/documents", tags=["documents"])
12+
api_router.include_router(document_lifecycle.router, prefix="/documents", tags=["lifecycle"])
13+
api_router.include_router(version_management.router, prefix="/documents", tags=["versions"])
14+
api_router.include_router(workflows.router, prefix="/workflows", tags=["workflows"])
15+
api_router.include_router(admin.router, prefix="/admin/documents", tags=["admin"])
1116

1217

1318
if settings.ENVIRONMENT == "local":

backend/app/api/routes/admin.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import uuid
2+
from typing import Any
3+
4+
from fastapi import APIRouter, Depends, HTTPException
5+
from sqlmodel import Session
6+
7+
from app.api.deps import CurrentUser, SessionDep, get_current_active_superuser
8+
from app.models import Document, AuditLog, Message
9+
from app.tasks.retention import archive_document, dispose_document
10+
11+
router = APIRouter()
12+
13+
@router.post("/{id}/archive", response_model=Message)
14+
async def manual_archive_document(
15+
*, session: SessionDep, current_user: CurrentUser, id: uuid.UUID
16+
) -> Any:
17+
"""
18+
Manually archive a document (superuser or owner).
19+
"""
20+
document = session.get(Document, id)
21+
if not document:
22+
raise HTTPException(status_code=404, detail="Document not found")
23+
24+
# Check ownership or superuser
25+
if document.owner_id != current_user.id and not current_user.is_superuser:
26+
raise HTTPException(status_code=403, detail="Not authorized")
27+
28+
await archive_document(session, document)
29+
return Message(message="Document archived successfully")
30+
31+
@router.post("/{id}/dispose", response_model=Message, dependencies=[Depends(get_current_active_superuser)])
32+
async def manual_dispose_document(
33+
*, session: SessionDep, current_user: CurrentUser, id: uuid.UUID
34+
) -> Any:
35+
"""
36+
Manually dispose of a document (GDPR-compliant deletion). Superuser only.
37+
"""
38+
document = session.get(Document, id)
39+
if not document:
40+
raise HTTPException(status_code=404, detail="Document not found")
41+
42+
await dispose_document(session, document)
43+
return Message(message="Document disposed successfully")
44+
45+
@router.post("/{id}/force-unlock", response_model=Message, dependencies=[Depends(get_current_active_superuser)])
46+
def force_unlock_document(
47+
*, session: SessionDep, id: uuid.UUID
48+
) -> Any:
49+
"""
50+
Force unlock a locked document. Admin/superuser only.
51+
"""
52+
from app.models import DocumentLock
53+
from sqlmodel import select
54+
55+
lock = session.exec(select(DocumentLock).where(DocumentLock.document_id == id)).first()
56+
if not lock:
57+
raise HTTPException(status_code=404, detail="Document is not locked")
58+
59+
session.delete(lock)
60+
session.commit()
61+
62+
return Message(message="Document unlocked successfully")

0 commit comments

Comments
 (0)