217 lines
6.0 KiB
Markdown
217 lines
6.0 KiB
Markdown
# Master Data Export/Import
|
|
|
|
## Overview
|
|
|
|
The export/import mechanism allows exporting all master data from the `model_gsmaster` and `model_learning_costs` modules to JSON files and importing them back. The exported data is ID-independent, using natural keys (name + game_system) to identify records, making it suitable for:
|
|
|
|
- Migrating data between environments
|
|
- Version controlling master data
|
|
- Manually editing game data
|
|
- Sharing/distributing game systems
|
|
|
|
## Supported Entities
|
|
|
|
### From `model_learning_costs.go`:
|
|
- **Sources** (`gsm_lit_sources`) - Game books and source materials
|
|
- **SkillCategories** (`learning_skill_categories`) - Skill classification categories
|
|
- **SkillDifficulties** (`learning_skill_difficulties`) - Difficulty levels
|
|
- **SkillCategoryDifficulties** (`learning_skill_category_difficulties`) - Relationship between skills, categories, and difficulties with learning costs
|
|
|
|
### From `model_gsmaster.go`:
|
|
- **Skills** (`gsm_skills`) - Character skills
|
|
- **Spells** (`gsm_spells`) - Magic spells
|
|
|
|
## Excluded Entities
|
|
|
|
The following are NOT exported/imported:
|
|
- `AuditLogEntry` - Audit logs (transient data)
|
|
- `BamortBase` - Base character data (character-specific)
|
|
- `BamortCharTrait` - Character traits (character-specific)
|
|
- `Magisch` - Character magic data (character-specific)
|
|
- `LookupList` - System lookup lists (system config)
|
|
|
|
## File Format
|
|
|
|
All data is exported to JSON files with indentation for easy reading and editing:
|
|
|
|
```json
|
|
[
|
|
{
|
|
"name": "Skill Name",
|
|
"game_system": "midgard",
|
|
"source_code": "KOD",
|
|
...
|
|
}
|
|
]
|
|
```
|
|
|
|
## Natural Keys
|
|
|
|
Instead of database IDs, the following natural keys are used to identify records:
|
|
|
|
| Entity | Natural Key |
|
|
|--------|------------|
|
|
| Source | `code` |
|
|
| Skill | `name` + `game_system` |
|
|
| Spell | `name` + `game_system` |
|
|
| SkillCategory | `name` + `game_system` |
|
|
| SkillDifficulty | `name` + `game_system` |
|
|
| SkillCategoryDifficulty | `skill_name` + `skill_system` + `category_name` + `category_system` + `difficulty_name` + `difficulty_system` |
|
|
|
|
## Usage
|
|
|
|
### Export
|
|
|
|
```go
|
|
import "bamort/gsmaster"
|
|
|
|
// Export specific entity types
|
|
err := gsmaster.ExportSources("/path/to/output")
|
|
err := gsmaster.ExportSkills("/path/to/output")
|
|
err := gsmaster.ExportSpells("/path/to/output")
|
|
err := gsmaster.ExportSkillCategories("/path/to/output")
|
|
err := gsmaster.ExportSkillDifficulties("/path/to/output")
|
|
err := gsmaster.ExportSkillCategoryDifficulties("/path/to/output")
|
|
|
|
// Export all master data at once
|
|
err := gsmaster.ExportAll("/path/to/output")
|
|
```
|
|
|
|
### Import
|
|
|
|
```go
|
|
import "bamort/gsmaster"
|
|
|
|
// Import specific entity types
|
|
err := gsmaster.ImportSources("/path/to/input")
|
|
err := gsmaster.ImportSkills("/path/to/input")
|
|
err := gsmaster.ImportSpells("/path/to/input")
|
|
err := gsmaster.ImportSkillCategories("/path/to/input")
|
|
err := gsmaster.ImportSkillDifficulties("/path/to/input")
|
|
err := gsmaster.ImportSkillCategoryDifficulties("/path/to/input")
|
|
|
|
// Import all master data at once
|
|
err := gsmaster.ImportAll("/path/to/input")
|
|
```
|
|
|
|
## Import Behavior
|
|
|
|
The import mechanism follows an "upsert" pattern:
|
|
|
|
1. **Check if record exists** using natural keys
|
|
2. If **not found**: Create new record
|
|
3. If **found**: Update existing record with imported values
|
|
|
|
This allows for:
|
|
- Importing new data
|
|
- Updating existing data
|
|
- Safe re-import of previously exported data
|
|
|
|
## Dependency Order
|
|
|
|
When using `ExportAll()` and `ImportAll()`, entities are processed in dependency order:
|
|
|
|
**Export/Import Order:**
|
|
1. Sources (no dependencies)
|
|
2. SkillCategories (depends on Sources)
|
|
3. SkillDifficulties (no dependencies)
|
|
4. Skills (depends on Sources)
|
|
5. SkillCategoryDifficulties (depends on Skills, Categories, Difficulties)
|
|
6. Spells (depends on Sources)
|
|
|
|
## File Names
|
|
|
|
Each entity type is exported to its own file:
|
|
|
|
| Entity | Filename |
|
|
|--------|----------|
|
|
| Sources | `sources.json` |
|
|
| Skills | `skills.json` |
|
|
| Spells | `spells.json` |
|
|
| SkillCategories | `skill_categories.json` |
|
|
| SkillDifficulties | `skill_difficulties.json` |
|
|
| SkillCategoryDifficulties | `skill_category_difficulties.json` |
|
|
|
|
## Example Workflow
|
|
|
|
### Exporting Data
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"bamort/gsmaster"
|
|
"log"
|
|
)
|
|
|
|
func main() {
|
|
outputDir := "./exported_data"
|
|
|
|
if err := gsmaster.ExportAll(outputDir); err != nil {
|
|
log.Fatalf("Export failed: %v", err)
|
|
}
|
|
|
|
log.Println("All master data exported to", outputDir)
|
|
}
|
|
```
|
|
|
|
### Editing Exported Data
|
|
|
|
```bash
|
|
# Edit the JSON files manually
|
|
vim exported_data/skills.json
|
|
```
|
|
|
|
### Importing Modified Data
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"bamort/gsmaster"
|
|
"bamort/database"
|
|
"log"
|
|
)
|
|
|
|
func main() {
|
|
// Initialize database connection
|
|
database.InitDB()
|
|
|
|
inputDir := "./exported_data"
|
|
|
|
if err := gsmaster.ImportAll(inputDir); err != nil {
|
|
log.Fatalf("Import failed: %v", err)
|
|
}
|
|
|
|
log.Println("All master data imported from", inputDir)
|
|
}
|
|
```
|
|
|
|
## Error Handling
|
|
|
|
All export/import functions return errors that should be checked:
|
|
|
|
- **Export errors**: Usually file system issues (permissions, disk space)
|
|
- **Import errors**: Can be:
|
|
- File not found
|
|
- Invalid JSON format
|
|
- Missing dependencies (e.g., referenced source doesn't exist)
|
|
- Database constraint violations
|
|
|
|
## Testing
|
|
|
|
The export/import mechanism is fully tested with TDD. See `gsmaster/export_import_test.go` for comprehensive test coverage including:
|
|
|
|
- Export creates valid JSON files
|
|
- Import creates new records
|
|
- Import updates existing records
|
|
- Relationships are correctly restored using natural keys
|
|
- Full export/import cycle works correctly
|
|
|
|
## Notes
|
|
|
|
- **No Handlers/Routes**: This functionality is intentionally not exposed as API endpoints. Use it programmatically or via CLI tools.
|
|
- **ID Independence**: Exported data does not contain database IDs, making it portable across different database instances.
|
|
- **Idempotent**: Import can be run multiple times safely - it will update existing records rather than creating duplicates.
|
|
- **Transaction Safety**: Each import operation should ideally be wrapped in a database transaction for atomicity.
|