6.0 KiB
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 (
learning_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:
[
{
"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
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
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:
- Check if record exists using natural keys
- If not found: Create new record
- 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:
- Sources (no dependencies)
- SkillCategories (depends on Sources)
- SkillDifficulties (no dependencies)
- Skills (depends on Sources)
- SkillCategoryDifficulties (depends on Skills, Categories, Difficulties)
- 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
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
# Edit the JSON files manually
vim exported_data/skills.json
Importing Modified Data
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.