325 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			325 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
# Presence Activity API Documentation
 | 
						|
 | 
						|
## Overview
 | 
						|
 | 
						|
The Presence Activity API allows users to manage their current activities (e.g., gaming, music, workouts) with automatic expiration through a lease-based system. Activities can be created, updated, and deleted, with support for flexible metadata and both system-generated and user-defined identifiers.
 | 
						|
 | 
						|
This service is handled by the DysonNetwork.Pass, when using with the gateway, replace the `/api` with the `/pass`
 | 
						|
 | 
						|
## Key Features
 | 
						|
 | 
						|
- **Lease-Based Expiration**: Activities automatically expire within 1-60 minutes unless renewed
 | 
						|
- **Flexible Identity**: Support for both autogenerated GUIDs and user-defined ManualIds
 | 
						|
- **Extensible Metadata**: JSON-stored metadata dictionary for custom developer data
 | 
						|
- **Soft Deletion**: Activities are soft-deleted and filtered automatically
 | 
						|
- **Performance Optimized**: Cached active activities with 1-minute expiration
 | 
						|
- **Authentication Required**: All endpoints require valid user authentication
 | 
						|
 | 
						|
## API Endpoints
 | 
						|
 | 
						|
### Base URL: `/api/activities`
 | 
						|
 | 
						|
### Authentication
 | 
						|
All endpoints require `[Authorize]` header. User context is automatically extracted.
 | 
						|
 | 
						|
---
 | 
						|
 | 
						|
## Get Active Activities
 | 
						|
 | 
						|
Retrieve all currently active (non-expired) presence activities for the authenticated user.
 | 
						|
 | 
						|
**Endpoint:** `GET /api/activities`
 | 
						|
 | 
						|
**Response:**
 | 
						|
```json
 | 
						|
[
 | 
						|
  {
 | 
						|
    "id": "550e8400-e29b-41d4-a716-446655440000",
 | 
						|
    "type": "Gaming",
 | 
						|
    "manualId": "game-session-1",
 | 
						|
    "title": "Playing Cyberpunk 2077",
 | 
						|
    "subtitle": "Night City Exploration",
 | 
						|
    "caption": "Missions completed: 15",
 | 
						|
    "meta": {
 | 
						|
      "appName": "Cyberpunk 2077",
 | 
						|
      "platform": "Steam",
 | 
						|
      "customProperty": "additional data"
 | 
						|
    },
 | 
						|
    "leaseMinutes": 10,
 | 
						|
    "leaseExpiresAt": "2024-01-15T14:30:00Z",
 | 
						|
    "accountId": "user-guid",
 | 
						|
    "createdAt": "2024-01-15T14:25:00Z",
 | 
						|
    "updatedAt": "2024-01-15T14:25:00Z",
 | 
						|
    "deletedAt": null
 | 
						|
  }
 | 
						|
]
 | 
						|
```
 | 
						|
 | 
						|
**Common Response Codes:**
 | 
						|
- `200 OK` - Success, returns array of active activities
 | 
						|
- `401 Unauthorized` - Invalid or missing authentication
 | 
						|
 | 
						|
---
 | 
						|
 | 
						|
## Create New Activity
 | 
						|
 | 
						|
Create a new presence activity with a configurable lease period.
 | 
						|
 | 
						|
**Endpoint:** `POST /api/activities`
 | 
						|
 | 
						|
**Request Body:**
 | 
						|
```json
 | 
						|
{
 | 
						|
  "type": "Gaming",
 | 
						|
  "manualId": "my-game-session",
 | 
						|
  "title": "Playing Cyberpunk 2077",
 | 
						|
  "subtitle": "Night City Mission",
 | 
						|
  "caption": "Currently exploring downtown",
 | 
						|
  "meta": {
 | 
						|
    "appName": "Cyberpunk 2077",
 | 
						|
    "platform": "Steam",
 | 
						|
    "difficulty": "Hard",
 | 
						|
    "mods": ["mod1", "mod2"]
 | 
						|
  },
 | 
						|
  "leaseMinutes": 15
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
**Response:** Returns the created `SnPresenceActivity` object with populated fields.
 | 
						|
 | 
						|
**Field Details:**
 | 
						|
- `type`: PresenceType enum (Unknown, Gaming, Music, Workout)
 | 
						|
- `manualId`: Optional user-defined string identifier
 | 
						|
- `title`, `subtitle`, `caption`: Display strings (max 4096 chars each)
 | 
						|
- `meta`: Optional `Dictionary<string, object>` for custom data
 | 
						|
- `leaseMinutes`: 1-60 minutes (default: 5)
 | 
						|
 | 
						|
**Response Codes:**
 | 
						|
- `200 OK` - Activity created successfully
 | 
						|
- `400 Bad Request` - Invalid lease minutes or malformed data
 | 
						|
- `401 Unauthorized` - Invalid authentication
 | 
						|
 | 
						|
---
 | 
						|
 | 
						|
## Update Activity
 | 
						|
 | 
						|
Update an existing activity using either its GUID or ManualId. Only provided fields are updated.
 | 
						|
 | 
						|
**Endpoint:** `PUT /api/activities`
 | 
						|
 | 
						|
**Query Parameters:** (one required)
 | 
						|
- `id` - System-generated GUID (string)
 | 
						|
- `manualId` - User-defined identifier (string)
 | 
						|
 | 
						|
**Request Body:** (all fields optional)
 | 
						|
```json
 | 
						|
{
 | 
						|
  "title": "Updated: Playing Cyberpunk 2077",
 | 
						|
  "meta": {
 | 
						|
    "appName": "Cyberpunk 2077",
 | 
						|
    "platform": "Steam",
 | 
						|
    "newProperty": "updated data"
 | 
						|
  },
 | 
						|
  "leaseMinutes": 20
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
**Response:** Returns the updated `SnPresenceActivity` object.
 | 
						|
 | 
						|
**Response Codes:**
 | 
						|
- `200 OK` - Activity updated successfully
 | 
						|
- `400 Bad Request` - Missing or invalid ID parameters
 | 
						|
- `401 Unauthorized` - Invalid authentication
 | 
						|
- `404 Not Found` - Activity not found or doesn't belong to user
 | 
						|
 | 
						|
**Example cURL:**
 | 
						|
```bash
 | 
						|
# Update by ManualId
 | 
						|
curl -X PUT "/api/activities?manualId=my-game-session" \
 | 
						|
  -H "Authorization: Bearer <token>" \
 | 
						|
  -H "Content-Type: application/json" \
 | 
						|
  -d '{"leaseMinutes": 20}'
 | 
						|
 | 
						|
# Update by GUID
 | 
						|
curl -X PUT "/api/activities?id=550e8400-e29b-41d4-a716-446655440000" \
 | 
						|
  -H "Authorization: Bearer <token>" \
 | 
						|
  -H "Content-Type: application/json" \
 | 
						|
  -d '{"title": "Updated Title"}'
 | 
						|
```
 | 
						|
 | 
						|
---
 | 
						|
 | 
						|
## Delete Activity
 | 
						|
 | 
						|
Soft-delete an activity using either GUID or ManualId.
 | 
						|
 | 
						|
**Endpoint:** `DELETE /api/activities`
 | 
						|
 | 
						|
**Query Parameters:** (one required)
 | 
						|
- `id` - System-generated GUID (string)
 | 
						|
- `manualId` - User-defined identifier (string)
 | 
						|
 | 
						|
**Request Body:** None
 | 
						|
 | 
						|
**Response:** No content (204)
 | 
						|
 | 
						|
**Response Codes:**
 | 
						|
- `204 No Content` - Activity deleted successfully
 | 
						|
- `400 Bad Request` - Missing or invalid ID parameters
 | 
						|
- `401 Unauthorized` - Invalid authentication
 | 
						|
- `404 Not Found` - Activity not found or doesn't belong to user
 | 
						|
 | 
						|
**Example cURL:**
 | 
						|
```bash
 | 
						|
# Delete by ManualId
 | 
						|
curl -X DELETE "/api/activities?manualId=my-game-session" \
 | 
						|
  -H "Authorization: Bearer <token>"
 | 
						|
```
 | 
						|
 | 
						|
---
 | 
						|
 | 
						|
## Additional Endpoint
 | 
						|
 | 
						|
### Get Activities by Account ID
 | 
						|
 | 
						|
**Endpoint:** `GET /api/activities/{accountId:guid}`
 | 
						|
 | 
						|
For administrative or debugging purposes. Returns activities for the specified account ID, regardless of authentication.
 | 
						|
 | 
						|
---
 | 
						|
 | 
						|
## Data Models
 | 
						|
 | 
						|
### PresenceType Enum
 | 
						|
```csharp
 | 
						|
public enum PresenceType
 | 
						|
{
 | 
						|
    Unknown,
 | 
						|
    Gaming,
 | 
						|
    Music,
 | 
						|
    Workout
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
### SnPresenceActivity
 | 
						|
```csharp
 | 
						|
public class SnPresenceActivity : ModelBase
 | 
						|
{
 | 
						|
    public Guid Id { get; set; } // System-generated GUID
 | 
						|
    public PresenceType Type { get; set; }
 | 
						|
    public string? ManualId { get; set; } // User-defined ID
 | 
						|
    public string? Title { get; set; }
 | 
						|
    public string? Subtitle { get; set; }
 | 
						|
    public string? Caption { get; set; }
 | 
						|
    public Dictionary<string, object>? Meta { get; set; } // JSON metadata
 | 
						|
    public int LeaseMinutes { get; set; } // Lease duration
 | 
						|
    public Instant LeaseExpiresAt { get; set; } // Expiration timestamp
 | 
						|
 | 
						|
    // Inherited from ModelBase
 | 
						|
    public Guid AccountId { get; set; }
 | 
						|
    public Instant CreatedAt { get; set; }
 | 
						|
    public Instant UpdatedAt { get; set; }
 | 
						|
    public Instant? DeletedAt { get; set; } // Soft deletion
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
## Behavior & Constraints
 | 
						|
 | 
						|
### Lease Expiration
 | 
						|
- Activities automatically expire when `SystemClock.Instance.GetCurrentInstant() > LeaseExpiresAt`
 | 
						|
- Expiry is checked in database queries, so expired activities are filtered out of GET operations
 | 
						|
- Clients must periodically update/renew leases to keep activities active
 | 
						|
 | 
						|
### ID Flexibility
 | 
						|
- **ManualId**: User-defined string, unique within a user's activities
 | 
						|
- **GUID**: System-generated, always unique, returned in API responses
 | 
						|
- Both can be used interchangeably for updates and deletion
 | 
						|
 | 
						|
### Performance Optimizations
 | 
						|
- Active activities are cached for 1 minute to handle frequent updates
 | 
						|
- Cache is invalidated on create/update/delete operations
 | 
						|
- Database queries filter expired activities automatically
 | 
						|
 | 
						|
### Security
 | 
						|
- All operations are scoped to the authenticated user's account
 | 
						|
- Users can only manage their own activities
 | 
						|
- Invalid or expired authentication tokens return 401 Unauthorized
 | 
						|
 | 
						|
### Data Storage
 | 
						|
- Activities are stored in PostgreSQL with JSONB metadata support
 | 
						|
- Soft deletion uses timestamp rather than hard removal
 | 
						|
- EF Core middleware automatically handles CreatedAt/UpdatedAt timestamps
 | 
						|
 | 
						|
## Usage Examples
 | 
						|
 | 
						|
### Gaming Session Management
 | 
						|
```javascript
 | 
						|
// Start gaming session
 | 
						|
const activity = await fetch('/api/activities', {
 | 
						|
  method: 'POST',
 | 
						|
  headers: { 'Content-Type': 'application/json' },
 | 
						|
  body: JSON.stringify({
 | 
						|
    type: 'Gaming',
 | 
						|
    manualId: 'game-session-1',
 | 
						|
    title: 'Playing Cyberpunk 2077',
 | 
						|
    meta: { appId: 'cyberpunk2077', mods: ['photorealistic'] },
 | 
						|
    leaseMinutes: 15
 | 
						|
  })
 | 
						|
});
 | 
						|
 | 
						|
// Update progress (extend lease)
 | 
						|
await fetch('/api/activities?manualId=game-session-1', {
 | 
						|
  method: 'PUT',
 | 
						|
  body: JSON.stringify({
 | 
						|
    title: 'Playing Cyberpunk 2077 - Level 25',
 | 
						|
    leaseMinutes: 15
 | 
						|
  })
 | 
						|
});
 | 
						|
 | 
						|
// End session
 | 
						|
await fetch('/api/activities?manualId=game-session-1', {
 | 
						|
  method: 'DELETE'
 | 
						|
});
 | 
						|
```
 | 
						|
 | 
						|
### Metadata Extension
 | 
						|
```javascript
 | 
						|
// Rich metadata support
 | 
						|
const activity = {
 | 
						|
  type: 'Music',
 | 
						|
  manualId: 'spotify-session',
 | 
						|
  title: 'Listening to Electronic',
 | 
						|
  meta: {
 | 
						|
    spotifyTrackId: '1Je1IMUlBXcx1FzbcXRuWw',
 | 
						|
    artist: 'Purity Ring',
 | 
						|
    album: 'Shrines',
 | 
						|
    duration: 240000, // milliseconds
 | 
						|
    custom: { userRating: 5, genre: 'Electronic' }
 | 
						|
  },
 | 
						|
  leaseMinutes: 30
 | 
						|
};
 | 
						|
```
 | 
						|
 | 
						|
## Error Handling
 | 
						|
 | 
						|
Common error responses follow REST API conventions:
 | 
						|
 | 
						|
```json
 | 
						|
{
 | 
						|
  "type": "Microsoft.AspNetCore.Mvc.ValidationProblemDetails",
 | 
						|
  "title": "One or more validation errors occurred.",
 | 
						|
  "status": 400,
 | 
						|
  "errors": {
 | 
						|
    "leaseMinutes": ["Lease minutes must be between 1 and 60."]
 | 
						|
  }
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
## Implementation Notes
 | 
						|
 | 
						|
- Built with ASP.NET Core and Entity Framework Core
 | 
						|
- Uses NodaTime for precise timestamp handling
 | 
						|
- PostgreSQL JSONB for flexible metadata storage
 | 
						|
- Integration with existing authentication and caching systems
 | 
						|
- Follows established project patterns for soft deletion and audit trails
 |