docs: add FileOrigin API reference and Kindred addon test suite
- Add docs/src/reference/cpp-file-origin.md: full API reference for the FileOrigin abstract interface, OriginType/ConnectionState enums, LocalFileOrigin behavior, and ownership detection algorithm - Add SUMMARY.md entry under new 'C++ API Reference' section - Add tests/test_kindred_pure.py: 78 pure-logic unit tests covering update_checker, datum_commands, spreadsheet_commands, silo_commands, silo_start, and silo_origin (no FreeCAD binary required) - Add tests/run_kindred_tests.py: two-tier test runner with CI exit codes - Add pixi task 'test-kindred' for running addon tests - Add CI/CD step in build.yml to run addon tests before build Closes #130
This commit is contained in:
199
docs/src/reference/cpp-file-origin.md
Normal file
199
docs/src/reference/cpp-file-origin.md
Normal file
@@ -0,0 +1,199 @@
|
||||
# FileOrigin — Abstract Interface
|
||||
|
||||
> **Header:** `src/Gui/FileOrigin.h`
|
||||
> **Implementation:** `src/Gui/FileOrigin.cpp`
|
||||
> **Namespace:** `Gui`
|
||||
|
||||
`FileOrigin` is the abstract base class that all document storage backends
|
||||
implement. It defines the contract for creating, opening, saving, and
|
||||
tracking FreeCAD documents through a pluggable origin system.
|
||||
|
||||
## Key Design Principle
|
||||
|
||||
Origins do **not** change where files are stored — all documents are always
|
||||
saved to the local filesystem. Origins change the **workflow** and
|
||||
**identity model**:
|
||||
|
||||
| Origin | Identity | Tracking | Authentication |
|
||||
|--------|----------|----------|----------------|
|
||||
| Local | File path | None | None |
|
||||
| PLM (Silo) | Database UUID | External DB + MinIO | Required |
|
||||
| Cloud | URL / key | External service | Required |
|
||||
|
||||
## Enums
|
||||
|
||||
### `OriginType`
|
||||
|
||||
```cpp
|
||||
enum class OriginType {
|
||||
Local, // Local filesystem storage
|
||||
PLM, // Product Lifecycle Management system (e.g., Silo)
|
||||
Cloud, // Generic cloud storage
|
||||
Custom // User-defined origin type
|
||||
};
|
||||
```
|
||||
|
||||
### `ConnectionState`
|
||||
|
||||
```cpp
|
||||
enum class ConnectionState {
|
||||
Disconnected, // Not connected
|
||||
Connecting, // Connection in progress
|
||||
Connected, // Successfully connected
|
||||
Error // Connection error occurred
|
||||
};
|
||||
```
|
||||
|
||||
Used by the [OriginSelectorWidget](./cpp-origin-selector-widget.md) to
|
||||
render status overlays on origin icons.
|
||||
|
||||
## Pure Virtual Methods (must override)
|
||||
|
||||
Every `FileOrigin` subclass must implement these methods.
|
||||
|
||||
### Identity
|
||||
|
||||
| Method | Return | Purpose |
|
||||
|--------|--------|---------|
|
||||
| `id()` | `std::string` | Unique origin ID (`"local"`, `"silo"`, …) |
|
||||
| `name()` | `std::string` | Display name for menus (`"Local Files"`, `"Kindred Silo"`) |
|
||||
| `nickname()` | `std::string` | Short label for toolbar (`"Local"`, `"Silo"`) |
|
||||
| `icon()` | `QIcon` | Icon for UI representation |
|
||||
| `type()` | `OriginType` | Classification enum |
|
||||
|
||||
### Workflow Characteristics
|
||||
|
||||
| Method | Return | Purpose |
|
||||
|--------|--------|---------|
|
||||
| `tracksExternally()` | `bool` | `true` if origin syncs to a remote system |
|
||||
| `requiresAuthentication()` | `bool` | `true` if origin needs login |
|
||||
|
||||
### Document Identity
|
||||
|
||||
| Method | Return | Purpose |
|
||||
|--------|--------|---------|
|
||||
| `documentIdentity(doc)` | `std::string` | Immutable tracking key. File path for Local, UUID for PLM. |
|
||||
| `documentDisplayId(doc)` | `std::string` | Human-readable ID. File path for Local, part number for PLM. |
|
||||
| `ownsDocument(doc)` | `bool` | Whether this origin owns the document. |
|
||||
|
||||
**Ownership detection** is the mechanism that determines which origin
|
||||
manages a given document. The
|
||||
[OriginManager](./cpp-origin-manager.md) calls `ownsDocument()` on each
|
||||
registered origin to resolve ownership.
|
||||
|
||||
- `LocalFileOrigin` owns documents that have **no** `SiloItemId` property
|
||||
on any object.
|
||||
- `SiloOrigin` (Python) owns documents where any object **has** a
|
||||
`SiloItemId` property.
|
||||
|
||||
### Core Document Operations
|
||||
|
||||
| Method | Parameters | Return | Purpose |
|
||||
|--------|-----------|--------|---------|
|
||||
| `newDocument` | `name = ""` | `App::Document*` | Create a new document |
|
||||
| `openDocument` | `identity` | `App::Document*` | Open by identity (non-interactive) |
|
||||
| `openDocumentInteractive` | — | `App::Document*` | Open via dialog (file picker / search) |
|
||||
| `saveDocument` | `doc` | `bool` | Save document |
|
||||
| `saveDocumentAs` | `doc, newIdentity` | `bool` | Save with new identity |
|
||||
| `saveDocumentAsInteractive` | `doc` | `bool` | Save via dialog |
|
||||
|
||||
Returns `nullptr` / `false` on failure or cancellation.
|
||||
|
||||
## Virtual Methods with Defaults (optional overrides)
|
||||
|
||||
### Capability Queries
|
||||
|
||||
These default to `false`. Override to advertise capabilities that the
|
||||
[CommandOrigin](./cpp-command-origin.md) commands check before enabling
|
||||
menu items.
|
||||
|
||||
| Method | Default | Enables |
|
||||
|--------|---------|---------|
|
||||
| `supportsRevisions()` | `false` | Commit / Pull / Push commands |
|
||||
| `supportsBOM()` | `false` | BOM command |
|
||||
| `supportsPartNumbers()` | `false` | Info command |
|
||||
| `supportsAssemblies()` | `false` | (reserved for future use) |
|
||||
|
||||
### Connection State
|
||||
|
||||
| Method | Default | Purpose |
|
||||
|--------|---------|---------|
|
||||
| `connectionState()` | `Connected` | Current connection state |
|
||||
| `connect()` | `return true` | Attempt to connect / authenticate |
|
||||
| `disconnect()` | no-op | Disconnect from origin |
|
||||
|
||||
### Extended PLM Operations
|
||||
|
||||
These default to no-op / `false`. Override in PLM origins.
|
||||
|
||||
| Method | Default | Purpose |
|
||||
|--------|---------|---------|
|
||||
| `commitDocument(doc)` | `false` | Create a versioned snapshot |
|
||||
| `pullDocument(doc)` | `false` | Fetch latest from remote |
|
||||
| `pushDocument(doc)` | `false` | Upload changes to remote |
|
||||
| `showInfo(doc)` | no-op | Show metadata dialog |
|
||||
| `showBOM(doc)` | no-op | Show Bill of Materials dialog |
|
||||
| `syncProperties(doc)` | `true` | Push property changes to backend |
|
||||
|
||||
## Signal
|
||||
|
||||
```cpp
|
||||
fastsignals::signal<void(ConnectionState)> signalConnectionStateChanged;
|
||||
```
|
||||
|
||||
Origins must emit this signal when their connection state changes. The
|
||||
`OriginSelectorWidget` subscribes to it to update the toolbar icon
|
||||
overlay (green = connected, red X = disconnected, warning = error).
|
||||
|
||||
## Construction and Lifetime
|
||||
|
||||
- `FileOrigin` is non-copyable (deleted copy constructor and assignment
|
||||
operator).
|
||||
- The protected default constructor prevents direct instantiation.
|
||||
- Instances are owned by `OriginManager` via `std::unique_ptr`.
|
||||
- Python origins are wrapped by `FileOriginPython` (see
|
||||
[Python-C++ bridge](./cpp-file-origin-python.md)).
|
||||
|
||||
## LocalFileOrigin
|
||||
|
||||
`LocalFileOrigin` is the built-in concrete implementation that ships with
|
||||
Kindred Create. It is always registered by `OriginManager` as the
|
||||
`"local"` origin and serves as the universal fallback.
|
||||
|
||||
### Behavior Summary
|
||||
|
||||
| Method | Behavior |
|
||||
|--------|----------|
|
||||
| `ownsDocument` | Returns `true` if **no** object has a `SiloItemId` property |
|
||||
| `documentIdentity` | Returns `doc->FileName` (full path) |
|
||||
| `newDocument` | Delegates to `App::GetApplication().newDocument()` |
|
||||
| `openDocument` | Delegates to `App::GetApplication().openDocument()` |
|
||||
| `openDocumentInteractive` | Shows standard FreeCAD file-open dialog with format filters |
|
||||
| `saveDocument` | Calls `doc->save()`, returns `false` if no filename set |
|
||||
| `saveDocumentAs` | Calls `doc->saveAs()` |
|
||||
| `saveDocumentAsInteractive` | Calls `Gui::Document::saveAs()` (shows save dialog) |
|
||||
|
||||
### Ownership Detection Algorithm
|
||||
|
||||
```
|
||||
for each object in document:
|
||||
if object has property "SiloItemId":
|
||||
return false ← owned by PLM, not local
|
||||
return true ← local owns this document
|
||||
```
|
||||
|
||||
This negative-match approach means `LocalFileOrigin` is the **universal
|
||||
fallback** — it owns any document that no other origin claims.
|
||||
|
||||
## See Also
|
||||
|
||||
- [OriginManager](./cpp-origin-manager.md) — singleton registry that
|
||||
manages origin instances
|
||||
- [FileOriginPython](./cpp-file-origin-python.md) — bridge for
|
||||
implementing origins in Python
|
||||
- [CommandOrigin](./cpp-command-origin.md) — File menu commands that
|
||||
dispatch to origins
|
||||
- [OriginSelectorWidget](./cpp-origin-selector-widget.md) — toolbar
|
||||
dropdown for switching origins
|
||||
- [Creating a Custom Origin (C++)](../guide/custom-origin-cpp.md)
|
||||
- [Creating a Custom Origin (Python)](../guide/custom-origin-python.md)
|
||||
Reference in New Issue
Block a user