Working with Entities
Entities are the fundamental data objects in Octostar — files, folders, people, organizations, or any custom concept type.
Entity Structure
Every entity has a common set of fields:
| Field | Description |
|---|---|
os_entity_uid | Primary unique identifier for the entity |
entity_id | Alternative identifier, used by drag-and-drop APIs |
os_concept | Concept type name (e.g. os_file, os_folder, os_image) |
entity_type | Synonym for os_concept on some API responses |
os_item_name | Human-readable display name |
The identifier field name varies by context:
getContext()returnsrecord.os_entity_uid, whileOsDropzoneentities exposeentity_id. Always check which API you're reading from.
Loading Entities from the Octostar Context
When the app launches from a context menu action, the entity the user right-clicked on is available via ContextAPI.getContext():
const { ContextAPI } = useOctostarContext()
useEffect(() => {
ContextAPI?.getContext().then((ctx) => {
const entityUid = (ctx as any)?.record?.os_entity_uid
// use entityUid to call your backend...
})
}, [ContextAPI])
Receiving Entities via Drag-and-Drop
Entities dragged in from the sidebar are delivered with entity_id:
import { OsDropzone } from '@octostar/platform-react'
import type { Entity } from '@octostar/platform-types'
<OsDropzone
accept={{ entities: true }}
onDropEntities={(entities: Entity[]) => {
const uid = entities[0]?.entity_id
}}
texts={{ idle: 'Drop an entity from the sidebar', active: 'Release to load' }}
/>
Looking Up Entities by UID (Backend)
On the backend, query a specific entity by UID using the ontology SQL client. The generic workspace item table covers all entity types:
async def lookup_entity_by_uid(client: OctostarClient, uid: str):
escaped = uid.replace("'", "''")
sql = f"""
SELECT * FROM etimbr.os_workspace_item
WHERE os_entity_uid = '{escaped}'
LIMIT 1
"""
results = await client.query_ontology(sql)
return results[0] if results else None
Entity-specific concept tables (e.g. timbr.person, timbr.os_file) can also be queried directly when you need concept-specific fields.
Distinguishing Entity Types
Use os_concept (or entity_type as a fallback) to check what kind of entity you're working with:
concept = record.get("os_concept") or record.get("entity_type")
is_folder = concept == "os_folder"
is_workspace = concept == "os_workspace"
is_file = not (is_folder or is_workspace)
Expanding Folders Recursively
When a user provides a folder entity, retrieve all files inside it. Track visited UIDs to prevent cycles:
def is_folder(item: dict) -> bool:
concept = item.get("os_concept") or item.get("entity_type")
return concept == "os_folder"
async def expand_folder(client, workspace_id, folder_uid, visited=None):
if visited is None:
visited = set()
if folder_uid in visited:
return []
visited.add(folder_uid)
items = await client.get_folder_files(workspace_id, folder_uid)
files = []
for item in items:
if is_folder(item):
files.extend(
await expand_folder(client, workspace_id, item["os_entity_uid"], visited)
)
else:
files.append(item)
return files