Skip to content

Commit 08928c1

Browse files
authored
Merge pull request #584 from ObedChibunna/feat/issues
2 parents 902f006 + 98a2864 commit 08928c1

41 files changed

Lines changed: 2804 additions & 186 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CONTRIBUTING.md

Lines changed: 152 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Thank you for your interest in contributing to Vaultix! This document provides g
1111
- [Coding Standards](#coding-standards)
1212
- [Testing](#testing)
1313
- [Documentation](#documentation)
14+
- [API Versioning Strategy](#api-versioning-strategy)
1415
- [Community](#community)
1516

1617
## Code of Conduct
@@ -68,13 +69,15 @@ chore/update-dependencies # Maintenance tasks
6869
```
6970

7071
**Examples:**
72+
7173
- `feat/add-dispute-resolution-modal`
7274
- `fix/wallet-connection-timeout`
7375
- `docs/contributing-guidelines`
7476

7577
### Making Changes
7678

7779
1. **Create a branch**:
80+
7881
```bash
7982
git checkout -b feat/your-feature-name
8083
```
@@ -84,6 +87,7 @@ chore/update-dependencies # Maintenance tasks
8487
3. **Write tests**: Add tests for new functionality
8588

8689
4. **Run checks locally**:
90+
8791
```bash
8892
pnpm turbo run lint test build
8993
```
@@ -107,6 +111,7 @@ footer (optional)
107111
```
108112

109113
**Types:**
114+
110115
- `feat`: New feature
111116
- `fix`: Bug fix
112117
- `docs`: Documentation changes
@@ -163,27 +168,33 @@ When creating a PR, use this template:
163168

164169
```markdown
165170
## Description
171+
166172
Brief description of changes and what problem this solves
167173

168174
## Related Issue
175+
169176
Closes #123
170177

171178
## Type of Change
179+
172180
- [ ] Bug fix (non-breaking change which fixes an issue)
173181
- [ ] New feature (non-breaking change which adds functionality)
174182
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
175183
- [ ] Documentation update
176184

177185
## Testing Done
186+
178187
- [ ] Unit tests added/updated
179188
- [ ] E2E tests added/updated
180189
- [ ] Manually tested locally
181190

182191
## Screenshots (if UI changes)
192+
183193
Before: [screenshot]
184194
After: [screenshot]
185195

186196
## Checklist
197+
187198
- [ ] My code follows the project's coding standards
188199
- [ ] I have run lint and tests locally
189200
- [ ] I have updated documentation as needed
@@ -219,13 +230,13 @@ After: [screenshot]
219230
async createEscrow(data: CreateEscrowDto): Promise<Escrow> {
220231
// Validate input
221232
await this.validateEscrowData(data);
222-
233+
223234
// Create escrow record
224235
const escrow = await this.escrowRepository.create(data);
225-
236+
226237
// Emit event
227238
await this.eventEmitter.emit('escrow.created', escrow);
228-
239+
229240
return escrow;
230241
}
231242
```
@@ -268,6 +279,7 @@ pub fn create_escrow(
268279
Organize code logically following the existing structure:
269280

270281
**Backend:**
282+
271283
```
272284
apps/backend/src/
273285
├── modules/ # Feature modules (auth, escrow, stellar, etc.)
@@ -279,6 +291,7 @@ apps/backend/src/
279291
```
280292

281293
**Frontend:**
294+
282295
```
283296
apps/frontend/
284297
├── app/ # Next.js app router pages
@@ -323,7 +336,7 @@ cd apps/onchain && cargo test
323336
**Backend Unit Test** (Jest):
324337

325338
```typescript
326-
describe('EscrowService', () => {
339+
describe("EscrowService", () => {
327340
let service: EscrowService;
328341

329342
beforeEach(async () => {
@@ -334,26 +347,28 @@ describe('EscrowService', () => {
334347
service = module.get<EscrowService>(EscrowService);
335348
});
336349

337-
it('should create escrow with valid data', async () => {
350+
it("should create escrow with valid data", async () => {
338351
const escrowData = {
339352
amount: 100,
340-
recipient: 'test-address',
341-
milestones: ['Milestone 1'],
353+
recipient: "test-address",
354+
milestones: ["Milestone 1"],
342355
};
343-
356+
344357
const result = await service.create(escrowData);
345-
358+
346359
expect(result).toBeDefined();
347-
expect(result.status).toBe('pending');
360+
expect(result.status).toBe("pending");
348361
expect(result.amount).toBe(100);
349362
});
350363

351-
it('should reject escrow with invalid amount', async () => {
352-
const invalidData = { /* ... */ };
353-
354-
await expect(service.create(invalidData))
355-
.rejects
356-
.toThrow(EscrowValidationError);
364+
it("should reject escrow with invalid amount", async () => {
365+
const invalidData = {
366+
/* ... */
367+
};
368+
369+
await expect(service.create(invalidData)).rejects.toThrow(
370+
EscrowValidationError,
371+
);
357372
});
358373
});
359374
```
@@ -367,24 +382,24 @@ import { CreateEscrowForm } from '@/components/escrow/create-escrow-form';
367382
describe('CreateEscrowForm', () => {
368383
it('submits form with valid data', async () => {
369384
const mockSubmit = jest.fn();
370-
385+
371386
render(<CreateEscrowForm onSubmit={mockSubmit} />);
372-
387+
373388
// Fill form
374389
fireEvent.change(screen.getByLabelText(/amount/i), {
375390
target: { value: '100' }
376391
});
377-
392+
378393
fireEvent.change(screen.getByLabelText(/recipient/i), {
379394
target: { value: 'GABC...DEF' }
380395
});
381-
396+
382397
// Submit
383398
fireEvent.click(screen.getByText('Create Escrow'));
384-
399+
385400
// Wait for submission
386401
await screen.findByText(/escrow created successfully/i);
387-
402+
388403
expect(mockSubmit).toHaveBeenCalledWith(
389404
expect.objectContaining({
390405
amount: 100,
@@ -395,10 +410,10 @@ describe('CreateEscrowForm', () => {
395410

396411
it('shows validation errors for invalid input', async () => {
397412
render(<CreateEscrowForm onSubmit={jest.fn()} />);
398-
413+
399414
// Submit empty form
400415
fireEvent.click(screen.getByText('Create Escrow'));
401-
416+
402417
expect(await screen.findByText(/amount is required/i))
403418
.toBeInTheDocument();
404419
});
@@ -412,20 +427,20 @@ describe('CreateEscrowForm', () => {
412427
fn test_create_escrow() {
413428
let env = Env::default();
414429
env.mock_all_auths();
415-
430+
416431
let contract_id = env.register_contract(None, VaultixEscrow);
417432
let depositor = Address::generate(&env);
418433
let recipient = Address::generate(&env);
419434
let amount = 1000_000_000; // 1 XLM in stroops
420-
435+
421436
// Create escrow
422437
let escrow_id = VaultixEscrowClient::new(&env, &contract_id)
423438
.create_escrow(&depositor, &recipient, &amount);
424-
439+
425440
// Verify escrow was created
426441
let escrow = VaultixEscrowClient::new(&env, &contract_id)
427442
.get_escrow(&escrow_id);
428-
443+
429444
assert_eq!(escrow.depositor, depositor);
430445
assert_eq!(escrow.recipient, recipient);
431446
assert_eq!(escrow.amount, amount);
@@ -435,6 +450,7 @@ fn test_create_escrow() {
435450
### Test Coverage Goals
436451

437452
Aim for high coverage on critical paths:
453+
438454
- ✅ Authentication flows (wallet connect, JWT)
439455
- ✅ Escrow creation and fund release
440456
- ✅ Milestone tracking and approval
@@ -450,13 +466,13 @@ Aim for high coverage on critical paths:
450466
```typescript
451467
/**
452468
* Validates and processes escrow milestone completion
453-
*
469+
*
454470
* @param escrowId - The ID of the escrow to update
455471
* @param milestoneIndex - Index of the milestone to complete
456472
* @param proofData - Optional proof of milestone completion
457-
*
473+
*
458474
* @returns Updated escrow entity
459-
*
475+
*
460476
* @throws {NotFoundError} If escrow doesn't exist
461477
* @throws {InvalidStateError} If escrow is not in active state
462478
*/
@@ -489,7 +505,7 @@ async completeMilestone(
489505
/// Returns `Error::Unauthorized` if caller is not authorized
490506
/// Returns `Error::InvalidState` if escrow conditions not met
491507
#[contractmethod]
492-
pub fn release_funds(e: &Env, escrow_id: u64, caller: Address)
508+
pub fn release_funds(e: &Env, escrow_id: u64, caller: Address)
493509
-> Result<Bytes, Error> {
494510
// Implementation
495511
}
@@ -498,12 +514,113 @@ pub fn release_funds(e: &Env, escrow_id: u64, caller: Address)
498514
### Updating README
499515

500516
Update README.md when:
517+
501518
- Adding new features or capabilities
502519
- Changing setup requirements or prerequisites
503520
- Modifying architecture or repository structure
504521
- Adding new configuration options
505522
- Updating deployment instructions
506523

524+
## API Versioning Strategy
525+
526+
Vaultix uses **URL-based API versioning** to ensure backward compatibility as the platform evolves.
527+
528+
### Current Version
529+
530+
The current stable API version is **v1**. All endpoints are prefixed with `/v1/`.
531+
532+
```
533+
GET /v1/escrows
534+
POST /v1/auth/verify
535+
GET /v1/admin/users
536+
```
537+
538+
### Version Lifecycle
539+
540+
| Version | Status | Sunset Date | Notes |
541+
| ------- | ---------- | ----------- | ----------------------------------- |
542+
| v1 | **Active** | - | Current stable API |
543+
| v2 | Scaffold | - | Placeholder endpoints, not yet live |
544+
545+
### Backward Compatibility
546+
547+
Unversioned requests (e.g., `/escrows`, `/auth/verify`) are automatically rewritten to `/v1/...` by the version negotiation middleware. These unversioned endpoints return a `Sunset` header indicating the deprecation deadline:
548+
549+
```
550+
Sunset: 2026-12-31T23:59:59.000Z
551+
Link: </v1/escrows>; rel="successor-version"
552+
X-API-Version: v1
553+
```
554+
555+
**All API consumers should migrate to versioned URLs before the sunset date.**
556+
557+
### Response Headers
558+
559+
| Header | Description |
560+
| --------------- | -------------------------------------------- |
561+
| `X-API-Version` | The resolved API version (e.g., `v1`) |
562+
| `Sunset` | RFC 8594 sunset date for deprecated versions |
563+
| `Deprecation` | Set to `true` when a version is deprecated |
564+
| `Link` | Points to the successor version URL |
565+
566+
### Adding a New Version
567+
568+
1. **Create controllers** with explicit version:
569+
570+
```typescript
571+
@Controller({ path: 'escrows', version: '2' })
572+
export class EscrowV2Controller { ... }
573+
```
574+
575+
2. **Register in a module** and import in `AppModule`.
576+
577+
3. **Update the version sunset map** in `api-version.middleware.ts`:
578+
579+
```typescript
580+
const API_VERSION_SUNSETS: Record<string, string | null> = {
581+
v1: null, // active
582+
v2: null, // active (new)
583+
};
584+
```
585+
586+
4. **Add to Swagger UI** version selector in `main.ts`.
587+
588+
5. **Deprecate old versions** by setting a sunset date:
589+
```typescript
590+
v1: '2027-06-30T23:59:59.000Z', // deprecated
591+
```
592+
593+
### Version-Neutral Endpoints
594+
595+
Some endpoints are version-neutral (accessible without a version prefix):
596+
597+
- `/health` - Health check probes
598+
- `/api/docs` - Swagger UI
599+
600+
Use `VERSION_NEUTRAL` in the controller decorator:
601+
602+
```typescript
603+
@Controller({ path: 'health', version: VERSION_NEUTRAL })
604+
```
605+
606+
### V2 Design Goals (Planned)
607+
608+
- Cursor-based pagination (replacing offset-based)
609+
- Envelope responses: `{ data, meta, links }`
610+
- Standardized filtering query parameters
611+
- RFC 7807 Problem Details error format
612+
- HATEOAS links for related resources
613+
614+
### Frontend Integration
615+
616+
The frontend API client automatically prepends `/v1` to all requests:
617+
618+
```typescript
619+
// lib/api-client.ts
620+
const API_VERSION_PREFIX = "/v1";
621+
const url = `${API_BASE_URL}${API_VERSION_PREFIX}${path}`;
622+
```
623+
507624
## Monorepo Tips
508625

509626
### Filtering Commands
@@ -544,13 +661,15 @@ pnpm add axios
544661
## Recognition
545662

546663
Contributors will be recognized in:
664+
547665
- README.md contributors section
548666
- Release notes
549667
- Annual contributor spotlight
550668

551669
## What We're Looking For
552670

553671
**High Priority Contributions:**
672+
554673
- ✅ Bug fixes (especially issues labeled `bug` or `priority: high`)
555674
- ✅ Test coverage improvements
556675
- ✅ Documentation enhancements
@@ -559,6 +678,7 @@ Contributors will be recognized in:
559678
- ✅ Security enhancements
560679

561680
**Post-MVP Features** (discuss before implementing):
681+
562682
- Multi-asset support (custom tokens, USDC)
563683
- Advanced analytics dashboard
564684
- Mobile applications

0 commit comments

Comments
 (0)