Zeeshaun a2a4d48de5
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 47s
Deploy Promiscuity Character API / deploy (push) Successful in 46s
Deploy Promiscuity Inventory API / deploy (push) Successful in 58s
Deploy Promiscuity Locations API / deploy (push) Successful in 44s
k8s smoke test / test (push) Successful in 7s
Inventory enhancements
2026-03-15 13:57:15 -05:00

240 lines
6.3 KiB
Markdown

# InventoryApi document shapes
This service stores one MongoDB document per inventory item record.
Inbound JSON documents
- CreateItemDefinitionRequest (`POST /api/inventory/item-definitions`)
```json
{
"itemKey": "wood",
"displayName": "Wood",
"stackable": true,
"maxStackSize": 20,
"category": "resource",
"equipSlot": null
}
```
- UpdateItemDefinitionRequest (`PUT /api/inventory/item-definitions/{itemKey}`)
```json
{
"displayName": "Wood",
"stackable": true,
"maxStackSize": 20,
"category": "resource",
"equipSlot": null
}
```
- GrantInventoryItemRequest (`POST /api/inventory/by-owner/{ownerType}/{ownerId}/grant`)
```json
{
"itemKey": "string",
"quantity": 1,
"preferredSlot": 0
}
```
`preferredSlot` is optional. If omitted, the service finds a valid destination slot.
- MoveInventoryItemRequest (`POST /api/inventory/by-owner/{ownerType}/{ownerId}/move`)
```json
{
"itemId": "uuid-string",
"toSlot": 1,
"quantity": 1
}
```
`quantity` is optional. If omitted, move the full stack.
- TransferInventoryItemRequest (`POST /api/inventory/transfer`)
```json
{
"itemId": "uuid-string",
"fromOwnerType": "character",
"fromOwnerId": "string",
"toOwnerType": "location",
"toOwnerId": "string",
"toSlot": 0,
"quantity": 1
}
```
`toSlot` and `quantity` are optional. If omitted, the service finds a valid destination and transfers the full stack.
- ConsumeInventoryItemRequest (`POST /api/inventory/items/{itemId}/consume`)
```json
{
"quantity": 1
}
```
- EquipInventoryItemRequest (`POST /api/inventory/items/{itemId}/equip`)
```json
{
"ownerId": "string",
"equipmentSlot": "weapon"
}
```
Only valid for items currently owned by a character inventory.
- UnequipInventoryItemRequest (`POST /api/inventory/items/{itemId}/unequip`)
```json
{
"ownerId": "string",
"preferredSlot": 0
}
```
Only valid for items currently equipped by a character.
Stored documents (MongoDB)
- ItemDefinition
```json
{
"itemKey": "wood",
"displayName": "Wood",
"stackable": true,
"maxStackSize": 20,
"category": "resource",
"equipSlot": null,
"createdUtc": "string (ISO-8601 datetime)",
"updatedUtc": "string (ISO-8601 datetime)"
}
```
- InventoryItem
```json
{
"id": "string (UUID)",
"itemKey": "wood",
"quantity": 12,
"ownerType": "character",
"ownerId": "string (ObjectId or stable external id)",
"ownerUserId": "string (ObjectId from auth token, null for public world owners)",
"slot": 0,
"equippedSlot": null,
"createdUtc": "string (ISO-8601 datetime)",
"updatedUtc": "string (ISO-8601 datetime)"
}
```
Equipped character item example:
```json
{
"id": "string (UUID)",
"itemKey": "pistol",
"quantity": 1,
"ownerType": "character",
"ownerId": "string (Character ObjectId)",
"ownerUserId": "string (ObjectId from auth token)",
"slot": null,
"equippedSlot": "weapon",
"createdUtc": "string (ISO-8601 datetime)",
"updatedUtc": "string (ISO-8601 datetime)"
}
```
Location stack example:
```json
{
"id": "string (UUID)",
"itemKey": "wood",
"quantity": 12,
"ownerType": "location",
"ownerId": "string (Location ObjectId)",
"ownerUserId": null,
"slot": 3,
"equippedSlot": null,
"createdUtc": "string (ISO-8601 datetime)",
"updatedUtc": "string (ISO-8601 datetime)"
}
```
Outbound JSON documents
- ItemDefinitionResponse
```json
{
"itemKey": "wood",
"displayName": "Wood",
"stackable": true,
"maxStackSize": 20,
"category": "resource",
"equipSlot": null,
"updatedUtc": "string (ISO-8601 datetime)"
}
```
- InventoryItemResponse
```json
{
"id": "string (UUID)",
"itemKey": "wood",
"quantity": 12,
"ownerType": "character",
"ownerId": "string",
"slot": 0,
"equippedSlot": null,
"updatedUtc": "string (ISO-8601 datetime)"
}
```
- InventoryOwnerResponse (`GET /api/inventory/by-owner/{ownerType}/{ownerId}`)
```json
{
"ownerType": "character",
"ownerId": "string",
"items": [
{
"id": "string (UUID)",
"itemKey": "wood",
"quantity": 12,
"ownerType": "character",
"ownerId": "string",
"slot": 0,
"equippedSlot": null,
"updatedUtc": "string (ISO-8601 datetime)"
}
]
}
```
- TransferInventoryResponse (`POST /api/inventory/transfer`)
```json
{
"movedItemId": "string (UUID)",
"fromOwnerType": "character",
"fromOwnerId": "string",
"toOwnerType": "location",
"toOwnerId": "string",
"fromItems": [],
"toItems": []
}
```
Validation rules
- `itemKey` must map to an existing item definition before item instances can be created
- `ownerType` must be a supported container type
- `ownerId` must map to an existing owning entity where applicable
- non-`SUPER` callers may only access owned character items unless explicit gameplay rules allow a world container read/write
- `quantity` must be greater than `0`
- non-stackable item definitions must have `maxStackSize = 1`
- non-stackable items must have `quantity = 1`
- equipped items must have `slot = null`
- unequipped bag items must have `equippedSlot = null`
- an item must not have both `slot` and `equippedSlot` populated
- slot occupancy must be unique for `(ownerType, ownerId, slot)` where `slot != null`
- equipment occupancy must be unique for `(ownerType, ownerId, equippedSlot)` where `equippedSlot != null`
Recommended indexes
- unique on `itemKey` for item definitions
- index on `(ownerType, ownerId)`
- unique on `(ownerType, ownerId, slot)` for bag slots
- unique on `(ownerType, ownerId, equippedSlot)` for equipped slots
- index on `itemKey` for inventory items
Behavior rules
- moving a full non-stackable item should update its owner and slot in place
- moving part of a stack should split the stack and create a new item record with a new UUID for the moved quantity
- moving into a compatible stack should merge quantities and delete or reduce the source record
- cross-owner transfer should be transactional when it mutates multiple records
- auctions should reference `itemId` values directly instead of copying item state into the auction document
- the service does not auto-seed item definitions; `ItemDefinitions` must be populated explicitly