
Te Docs
- 35 installs
- 836 repo stars
- Updated July 29, 2026
- data-goblin/power-bi-agentic-development
Search Tabular Editor documentation and understand TE3 configuration files like .tmuo, Preferences.json, and Layouts.json.
About
Guidance for searching Tabular Editor documentation and understanding TE3 configuration files such as .tmuo, Preferences.json, and Layouts.json. A developer uses it to find TE how-to guidance or configure per-model TE3 settings.
- Searches Tabular Editor documentation for how-to guidance
- Explains TE3 config files (.tmuo, Preferences.json)
Te Docs by the numbers
- 35 all-time installs (skills.sh)
- Ranked #1,060 of 2,064 Data Science & ML skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/data-goblin/power-bi-agentic-development --skill te-docsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 35 |
|---|---|
| repo stars | ★ 836 |
| Last updated | July 29, 2026 |
| Repository | data-goblin/power-bi-agentic-development ↗ |
What it does
Search Tabular Editor documentation and understand TE3 configuration files like .tmuo, Preferences.json, and Layouts.json.
Files
Tabular Editor Documentation & Configuration
Guidance for searching Tabular Editor documentation and understanding TE3 configuration files (.tmuo, Preferences.json, etc.).
Pre-flight
Before using documentation search, verify pbi-search is installed:
pbi-search --versionIf the command is not found, inform the user and offer two options:
1. Install `pbi-search` (recommended): see bin/README.md for install instructions via cargo install or GitHub Releases at data-goblin/pbi-search 2. Search without the CLI: use the microsoft-learn MCP tools (microsoft_docs_search, microsoft_docs_fetch) for Microsoft Learn content, or WebFetch to retrieve docs directly from these sources:
- Tabular Editor docs:
https://docs.tabulareditor.com/ - DAX reference:
https://dax.guide/<function>/ - SQLBI articles:
https://www.sqlbi.com/articles/ - Data Goblins:
https://data-goblins.com/
The CLI is strongly preferred; it searches all sources simultaneously and returns clean markdown. The fallback requires manual URL construction and multiple fetches.
Documentation Search
Use the pbi-search CLI — the preferred way to search Tabular Editor docs and related Power BI/DAX resources. It searches Tabular Editor docs, DAX.guide, SQLBI, Microsoft Learn (Power BI + Fabric), the TE blog, and Data Goblins simultaneously, returning clean markdown.
After install, populate the local manifest cache (once):
pbi-search sync # ~13sSearching
# Search all sources
pbi-search search "creating measures"
# Search only Tabular Editor docs
pbi-search search "BPA rules" --source te-docs
# Search TE blog + TE docs
pbi-search search "incremental refresh" --source te-docs --source te-blog
# JSON output for structured use in agents
pbi-search search "workspace mode" --source te-docs --json
# Include content excerpts
pbi-search search "calculated columns" --source te-docs --excerptsFetching full docs
# Tabular Editor doc by bare path (from search results)
pbi-search fetch features/Best-Practice-Analyzer
# Any supported URL
pbi-search fetch https://docs.tabulareditor.com/features/workspace-mode
pbi-search fetch https://dax.guide/calculate/
# Extract a specific section
pbi-search fetch features/Best-Practice-Analyzer --section "Creating rules"
# Truncate for context budget
pbi-search fetch features/creating-measures --max-chars 3000 --jsonAgent search workflow
1. pbi-search search "<topic>" --source te-docs --json — find relevant docs 2. Use the path or url from results: pbi-search fetch <path> 3. No results? Broaden: pbi-search search "<topic>" (all sources) 4. DAX questions: always add --source dax-guide
Available sources
| ID | Content |
|---|---|
te-docs | Tabular Editor docs (features, how-tos, KB, references) |
dax-guide | ~480 DAX function reference pages |
te-blog | Tabular Editor blog |
ms-learn | Microsoft Learn — Power BI + Fabric (live, no sync needed) |
sqlbi | ~370 SQLBI technical articles |
data-goblins | Data Goblins Power BI posts |
Richer search quality (optional)
Default sync builds a fast title-only index. For conceptual queries ("remove filters from column") run once with descriptions:
pbi-search sync --descriptions # fetches meta descriptions; ~30s extra---
Configuration Files (.tmuo)
TMUO files store developer- and model-specific preferences in Tabular Editor 3.
Critical
- TMUO files contain user-specific settings -- never commit to version control
- Credentials are encrypted with Windows User Key -- cannot be shared between users
- Add
*.tmuoto.gitignorein all projects - File naming:
<ModelFileName>.<WindowsUserName>.tmuo
Structure
{
"UseWorkspace": true,
"WorkspaceConnection": "localhost",
"WorkspaceDatabase": "MyModel_Workspace_JohnDoe",
"Deployment": {
"TargetConnectionString": "powerbi://api.powerbi.com/v1.0/myorg/Workspace",
"TargetDatabase": "MyModel",
"DeployPartitions": false,
"DeployModelRoles": true
},
"DataSourceOverrides": {
"SQL Server": {
"ConnectionString": "Data Source=localhost;Initial Catalog=DevDB"
}
}
}Sections
| Section | Purpose |
|---|---|
UseWorkspace | Enable workspace database mode |
WorkspaceConnection | Server for workspace database |
WorkspaceDatabase | Workspace database name (unique per dev/model) |
Deployment | Target server, database, and deploy options |
DataSourceOverrides | Override connections for workspace |
TableImportSettings | Settings for Import Tables feature |
Deployment Options
| Field | Type | Description |
|---|---|---|
TargetConnectionString | string | Target server connection |
TargetDatabase | string | Target database name |
DeployPartitions | bool | Deploy partition definitions |
DeployModelRoles | bool | Deploy security roles |
DeployModelRoleMembers | bool | Deploy role members |
DeploySharedExpressions | bool | Deploy shared M expressions |
Application Preferences
TE3 stores application-level preferences in %LocalAppData%\TabularEditor3\:
| File | Purpose |
|---|---|
Preferences.json | Application settings (proxy, updates, telemetry) |
UiPreferences.json | UI state (window positions, panel sizes) |
Layouts.json | Saved layout configurations |
References
- `references/doc-structure.md` -- Detailed documentation structure
- `references/url-redirects.md` -- Old-to-new URL mapping for broken links
- `schema/` -- JSON schemas for tmuo, preferences, layouts, UI preferences
- `scripts/validate_config.py` -- Validate TE3 config files
- `scripts/validate_tmuo.py` -- Validate TMUO files
External
pbi-search
`pbi-search` is a documentation search CLI for Power BI, Tabular Editor, DAX, and Fabric. The te-docs skill depends on it for documentation search.
Install
From source (recommended)
Requires Rust:
cargo install --git https://github.com/data-goblin/pbi-search
pbi-search syncFrom GitHub Releases
Download the binary for your platform from the pbi-search releases page, place it on your PATH, then run:
pbi-search syncFirst run
After installing, populate the local manifest cache:
pbi-search sync # ~13sFor richer search results (optional):
pbi-search sync --descriptions # ~30s extraTabularEditorDocs Repository Structure
Clone path: <DOCS_PATH> (configure via environment variable TABULAR_EDITOR_DOCS or use your local clone location)
Top-Level Structure
TabularEditorDocs/
├── content/ # All documentation markdown files
├── _site/ # Built HTML output (generated)
├── configuration/ # Build configuration
├── templates/ # DocFX templates
├── redirects.json # URL redirect mappings
└── gen_redirects.py # Redirect generation scriptContent Directory
features/
Feature documentation for Tabular Editor 2 and 3.
| Subdirectory | Content |
|---|---|
CSharpScripts/ | C# script library (Beginner, Advanced, Templates) |
views/ | UI views (BPA, diagram, data refresh, properties, etc.) |
Semantic Model/ | Semantic model types, Direct Lake, DirectQuery |
Key files:
dax-scripts.md- DAX scripting featuredax-editor.md- DAX editor featuresdax-debugger.md- DAX debuggingcsharp-scripts.md- C# scripting overviewtmdl.md- TMDL supportdeployment.md- Deployment featuresBest-Practice-Analyzer.md- BPA feature overviewusing-bpa-sample-rules-expressions.md- BPA expression examples
getting-started/
Onboarding and setup documentation.
Key files:
bpa.md- BPA introduction and setupinstallation.md- Installation guidegeneral-introduction.md- Tabular Editor overviewdax-script-introduction.md- DAX scripts introcs-scripts-and-macros.md- C# scripts and macros intromigrate-from-desktop.md- Migration from Power BI Desktopmigrate-from-te2.md- Migration from TE2 to TE3workspace-mode.md- Workspace mode introduction
tutorials/
Step-by-step tutorials.
| Subdirectory | Content |
|---|---|
data-security/ | RLS, OLS setup and testing |
incremental-refresh/ | Incremental refresh setup and management |
Key files:
calendars.md- Calendar table creationudfs.md- User-defined functionsdirect-lake-guidance.md- Direct Lake best practicespowerbi-xmla.md- Power BI XMLA endpoint usagenew-pbi-model.md- Creating new Power BI modelsconnecting-to-azure-databricks.md- Databricks integration
how-tos/
Task-specific guides.
Key files:
Advanced-Scripting.md- Advanced scripting techniquesImporting-Tables.md- Table import proceduresMaster-model-pattern.md- Master model development patternxmla-as-connectivity.md- XMLA/AS connectivitypowerbi-xmla-pbix-workaround.md- PBIX workarounds
references/
Reference documentation.
| Subdirectory | Content |
|---|---|
release-notes/ | Version release notes (3_0_1.md through 3_24_2.md) |
Key files:
preferences.md- All TE3 preferences/settingsshortcuts3.md- TE3 keyboard shortcutsdownloads.md- Download linksrelease-history.md- Version historyFAQ.md- Frequently asked questionswhats-new.md- What's new overview
kb/
Knowledge base articles.
BPA Rules:
bpa-*.md- Individual BPA rule explanations
Error Codes:
DI*.md- Data import errorsDR*.md- Data refresh errorsRW*.md- Read/write errors
troubleshooting/
Problem resolution guides.
Key files:
licensing-activation.md- License issuesproxy-settings.md- Proxy configurationdirect-lake-entity-updates-reverting.md- Direct Lake issues
security/
Security and privacy documentation.
Key files:
security-privacy.md- Security featuresprivacy-policy.md- Privacy policythird-party-notices.md- Third-party components
Search Tips
Find BPA Content
rg -i "bpa|best.practice" content/ --type md -lFind C# Script Examples
ls content/features/CSharpScripts/
rg -i "example|snippet" content/features/CSharpScripts/ --type mdFind Preferences/Settings
rg -i "preference|setting" content/references/preferences.md -C 3Find Release Notes for Feature
rg -i "feature-name" content/references/release-notes/ --type mdFind KB Article by Error
rg -i "error message" content/kb/ --type mdTabular Editor URL Redirects
The docs site underwent a major reorganization. This file maps old URLs to new paths.
Note: The web server returns HTTP 404 for old URLs instead of proper 301 redirects, causing issues for AI agents and automated tools. Use this mapping to find the correct local file path.
Key BPA Redirects
| Old URL | New URL | Local File |
|---|---|---|
/common/using-bpa.html | /getting-started/bpa.html | content/getting-started/bpa.md |
/onboarding/bpa.html | /getting-started/bpa.html | content/getting-started/bpa.md |
/te3/views/bpa-view.html | /features/views/bpa-view.html | content/features/views/bpa-view.md |
/te2/Best-Practice-Analyzer.html | /features/Best-Practice-Analyzer.html | content/features/Best-Practice-Analyzer.md |
/common/using-bpa-sample-rules-expressions.html | /features/using-bpa-sample-rules-expressions.html | content/features/using-bpa-sample-rules-expressions.md |
Section Mappings
| Old Path Pattern | New Path Pattern |
|---|---|
/common/* | /features/* or /getting-started/* |
/onboarding/* | /getting-started/* |
/te2/* | Various (/features/, /how-tos/, /references/) |
/te3/features/* | /features/* |
/te3/views/* | /features/views/* |
/te3/tutorials/* | /tutorials/* |
/te3/other/release-notes/* | /references/release-notes/* |
Complete Redirect Table
| Old URL | New URL |
|---|---|
/common/CSharpScripts/* | /features/CSharpScripts/* |
/common/desktop-limitations.html | /getting-started/desktop-limitations.html |
/common/policies.html | /references/policies.html |
/common/save-to-folder.html | /features/save-to-folder.html |
/common/script-helper-methods.html | /features/script-helper-methods.html |
/common/xmla-as-connectivity.html | /how-tos/xmla-as-connectivity.html |
/onboarding/boosting-productivity-te3.html | /getting-started/boosting-productivity-te3.html |
/onboarding/creating-and-testing-dax.html | /getting-started/creating-and-testing-dax.html |
/onboarding/cs-scripts-and-macros.html | /getting-started/cs-scripts-and-macros.html |
/onboarding/dax-script-introduction.html | /getting-started/dax-script-introduction.html |
/onboarding/general-introduction.html | /getting-started/general-introduction.html |
/onboarding/importing-tables-data-modeling.html | /getting-started/importing-tables-data-modeling.html |
/onboarding/installation.html | /getting-started/installation.html |
/onboarding/migrate-from-desktop.html | /getting-started/migrate-from-desktop.html |
/onboarding/migrate-from-te2.html | /getting-started/migrate-from-te2.html |
/onboarding/parallel-development.html | /getting-started/parallel-development.html |
/onboarding/personalizing-te3.html | /getting-started/personalizing-te3.html |
/onboarding/refresh-preview-query.html | /getting-started/refresh-preview-query.html |
/te2/Advanced-Filtering-of-the-Explorer-Tree.html | /how-tos/Advanced-Filtering-of-the-Explorer-Tree.html |
/te2/Advanced-Scripting.html | /how-tos/Advanced-Scripting.html |
/te2/Best-Practice-Analyzer-Improvements.html | /features/Best-Practice-Analyzer.html |
/te2/Command-line-Options.html | /features/Command-line-Options.html |
/te2/FAQ.html | /references/FAQ.html |
/te2/Getting-Started.html | /getting-started/Getting-Started-te2.html |
/te2/Importing-Tables.html | /how-tos/Importing-Tables.html |
/te2/Keyboard-Shortcuts.html | /references/Keyboard-Shortcuts2.html |
/te2/Master-model-pattern.html | /how-tos/Master-model-pattern.html |
/te2/Useful-script-snippets.html | /features/Useful-script-snippets.html |
/te2/Workspace-Database.html | /features/Workspace-Database.html |
/te3/features/code-actions.html | /features/code-actions.html |
/te3/features/csharp-scripts.html | /features/csharp-scripts.html |
/te3/features/dax-debugger.html | /features/dax-debugger.html |
/te3/features/dax-editor.html | /features/dax-editor.html |
/te3/features/dax-optimizer-integration.html | /features/dax-optimizer-integration.html |
/te3/features/dax-query.html | /features/dax-query.html |
/te3/features/dax-scripts.html | /features/dax-scripts.html |
/te3/features/deployment.html | /features/deployment.html |
/te3/features/diagram-view.html | /features/views/diagram-view.html |
/te3/features/preferences.html | /references/preferences.html |
/te3/features/shortcuts.html | /references/shortcuts3.html |
/te3/features/tmdl.html | /features/tmdl.html |
/te3/getting-started.html | /getting-started/getting-started.html |
/te3/tutorials/calendars.html | /tutorials/calendars.html |
/te3/tutorials/udfs.html | /tutorials/udfs.html |
/te3/tutorials/workspace-mode.html | /features/workspace-mode.partial.html |
Source
Full redirect mapping is in <DOCS_PATH>/redirects.json (in your local TabularEditorDocs clone)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://github.com/TabularEditor/TabularEditor3/Layouts.json",
"title": "Tabular Editor 3 Layouts Configuration",
"description": "Schema for Tabular Editor 3 window layout configurations. Stores panel arrangements, docking positions, and menu customizations.",
"$comment": "Version 1.0.0 - Schema location is temporary until schemas repo is available.",
"type": "object",
"required": ["Version", "Layouts"],
"properties": {
"Version": {
"type": "integer",
"description": "Schema version for the layouts file format.",
"minimum": 1
},
"Layouts": {
"type": "array",
"description": "Array of saved layout configurations.",
"items": {
"type": "object",
"required": ["Name"],
"properties": {
"Name": {
"type": "string",
"description": "Name of the layout configuration."
},
"Panels": {
"type": "array",
"description": "Array of panel configurations.",
"items": {
"$ref": "#/definitions/Panel"
}
},
"Bars": {
"type": "array",
"description": "Toolbar configurations.",
"items": {
"$ref": "#/definitions/Bar"
}
},
"Menus": {
"type": "array",
"description": "Menu customizations.",
"items": {
"$ref": "#/definitions/Menu"
}
}
}
}
}
},
"definitions": {
"Panel": {
"type": "object",
"properties": {
"Type": {
"type": "string",
"description": "Panel type identifier.",
"enum": [
"TabularExplorer", "BestPracticeAnalyzer", "GitChanges", "Messages",
"ModelProcessing", "VertiPaqAnalyzer", "ManageMacros", "QuickEditor",
"PerspectiveEditor", "MetadataTranslationEditor", "DaxOptimizer",
"DaxPackageManager", "DaxDependencies", "FindReplace", "CalendarEditor",
"WhatsNew", "PropertyGrid"
]
},
"Dock": {
"type": "string",
"description": "Docking position.",
"enum": ["Left", "Right", "Top", "Bottom", "Float"]
},
"Mode": {
"type": "string",
"description": "Panel mode.",
"enum": ["Tabbed", "Split"]
},
"Visibility": {
"type": "string",
"description": "Panel visibility state.",
"enum": ["Visible", "Hidden", "AutoHide"]
},
"SizeFactor": {
"type": "number",
"description": "Relative size factor (0-1).",
"minimum": 0,
"maximum": 1
},
"TabbedDocumentGroup": {
"type": "integer",
"description": "Tabbed document group index."
},
"DockedAsTabbedDocument": {
"type": "boolean",
"description": "Whether panel is docked as a tabbed document."
},
"SavedTabbed": {
"type": "boolean",
"description": "Whether panel was previously tabbed."
},
"Children": {
"type": "array",
"description": "Child panels.",
"items": {
"$ref": "#/definitions/Panel"
}
},
"FloatPosition": {
"$ref": "#/definitions/FloatPosition"
}
}
},
"FloatPosition": {
"type": "object",
"description": "Position and size for floating panels.",
"properties": {
"Top": { "type": "integer" },
"Left": { "type": "integer" },
"Width": { "type": "integer" },
"Height": { "type": "integer" }
}
},
"Bar": {
"type": "object",
"properties": {
"Bar": { "type": "string", "description": "Toolbar identifier." },
"Caption": { "type": "string", "description": "Toolbar caption." },
"Col": { "type": "integer", "description": "Column position." },
"Row": { "type": "integer", "description": "Row position." },
"Links": { "type": "array", "items": { "type": "object" } }
}
},
"Menu": {
"type": "object",
"properties": {
"Menu": { "type": "string", "description": "Menu identifier." },
"Links": {
"type": "array",
"description": "Menu item modifications.",
"items": {
"type": "object",
"properties": {
"Item": { "type": "string" },
"Change": { "type": "string", "enum": ["Added", "Removed"] },
"Caption": { "type": "string" },
"BeginGroup": { "type": "boolean" }
}
}
}
}
}
}
}
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://github.com/TabularEditor/TabularEditor3/Preferences.json",
"title": "Tabular Editor 3 Preferences",
"description": "Schema for Tabular Editor 3 application preferences. Stores deployment settings, data browsing options, TMDL configuration, and proxy settings.",
"$comment": "Version 1.0.0 - Schema location is temporary until schemas repo is available.",
"type": "object",
"properties": {
"Version": {
"type": "integer",
"description": "Schema version for the preferences file format."
},
"AppVersion": {
"type": "string",
"description": "Tabular Editor version that created this preferences file.",
"pattern": "^\\d+\\.\\d+\\.\\d+$"
},
"DaxOptimizer": {
"type": "object",
"description": "DAX Optimizer settings.",
"properties": {
"ObfuscateVpax": { "type": "boolean" }
}
},
"Deployment": {
"type": "object",
"description": "Default deployment settings.",
"properties": {
"DeployDataSources": { "type": "boolean" },
"DeployPartitions": { "type": "boolean" },
"DeployRefreshPolicyPartitions": { "type": "boolean" },
"DeployModelRoles": { "type": "boolean" },
"DeployModelRoleMembers": { "type": "boolean" },
"DeploySharedExpressions": { "type": "boolean" }
}
},
"Defaults": {
"type": "object",
"description": "Default settings for new models.",
"properties": {
"NewModelCL": {
"type": "integer",
"description": "Default compatibility level for new models."
},
"UseLatestDefault": { "type": "boolean" },
"NewModelUseWorkspace": { "type": "boolean" }
}
},
"DataBrowsing": {
"type": "object",
"description": "Data browsing and query settings.",
"properties": {
"AutoRefreshDataPreview": { "type": "boolean" },
"AutoRefreshPivotGrid": { "type": "boolean" },
"PivotCustomizationShowAllFields": { "type": "boolean" },
"PivotHeaderWordWrap": { "type": "boolean" },
"WarnIfPivotGridFieldsMismatch": { "type": "boolean" },
"AlwaysShowPivotGridFieldList": { "type": "boolean" },
"PivotCustomizationDefaultLayout": { "type": "string" },
"AutoExecuteDaxQuery": { "type": "boolean" },
"DirectQueryMaxRows": { "type": "integer" },
"DaxQueryMaxRows": { "type": "integer" },
"DaxQuerySmartSelection": { "type": "boolean" },
"KeepFilteringAndSortingDaxQuery": { "type": "string" }
}
},
"PbiEndpointBaseUrl": {
"type": "string",
"description": "Power BI API endpoint URL.",
"format": "uri"
},
"FabricEndpointBaseUrl": {
"type": "string",
"description": "Microsoft Fabric API endpoint URL.",
"format": "uri"
},
"PbiDefaultAuthMode": {
"type": "string",
"description": "Default authentication mode for Power BI connections.",
"enum": ["Integrated", "UsernamePassword", "ServicePrincipal"]
},
"DefaultSaveMode": {
"type": "string",
"description": "Default format when saving models.",
"enum": ["TMDL", "BIM", "Folder"]
},
"DaxFormatterRequestTimeout": {
"type": "integer",
"description": "Timeout in milliseconds for DAX Formatter requests."
},
"DaxFormatterConsent": { "type": "boolean" },
"CheckForUpdatesOnStartup": { "type": "boolean" },
"CheckForUpdatesMajorOnly": { "type": "boolean" },
"VertiPaqAnalyzerIncludeTom": { "type": "boolean" },
"VertiPaqAnalyzerReadStatsFromData": { "type": "boolean" },
"VertiPaqAnalyzerDirectLakeExtractionMode": {
"type": "string",
"enum": ["Full", "Partial", "None"]
},
"VertiPaqAnalyzerReadStatsFromDQ": { "type": "boolean" },
"VertiPaqAnalyzerSampleRI": { "type": "integer" },
"VertiPaqAnalyzerColumnBatchSize": { "type": "integer" },
"CollectTelemetry": { "type": "boolean" },
"FormulaFixup": { "type": "boolean" },
"FormulaFixupOnPaste": { "type": "boolean" },
"PbiModelsAddDaxLineBreak": { "type": "boolean" },
"PbiModelsAddDaxLineBreakMultiLineOnly": { "type": "boolean" },
"AllowUnsupportedPBIFeatures": { "type": "boolean" },
"HidePBIAutoDateTimeWarnings": { "type": "boolean" },
"WarnWhenMetadataOutOfSync": { "type": "boolean" },
"ChangeDetectionOnLocalServers": { "type": "boolean" },
"AutoRefreshTomTree": { "type": "boolean" },
"BackgroundBpa": { "type": "boolean" },
"BuiltInBpaRules": {
"type": "string",
"enum": ["Enable", "Disable"]
},
"DisabledBuiltInRuleIds": {
"type": "array",
"description": "List of disabled built-in BPA rule IDs.",
"items": { "type": "string" }
},
"DataRefreshNotification": { "type": "boolean" },
"SaveBackupLocation": { "type": "string" },
"AnnotateDeploymentMetadata": { "type": "boolean" },
"SendErrorReports": { "type": "boolean" },
"SchemaCompare_IgnoreImport": { "type": "boolean" },
"SchemaCompare_IgnoreDataTypeChanges": { "type": "boolean" },
"SchemaCompare_IgnoreDescriptionChanges": { "type": "boolean" },
"SchemaCompare_IgnoreDecimalToDoubleChanges": { "type": "boolean" },
"DirectLake_AutoRefreshSave": { "type": "boolean" },
"PrioritizeAnalysisServicesSchemaDetector": { "type": "boolean" },
"IgnoreLineageTags": { "type": "boolean" },
"IgnoreInferredObjects": { "type": "boolean" },
"IgnoreInferredProperties": { "type": "boolean" },
"IgnoreTimestamps": { "type": "boolean" },
"IgnorePrivacySettings": { "type": "boolean" },
"IncludeSensitive": { "type": "boolean" },
"IgnoreIncrementalRefreshPartitions": { "type": "boolean" },
"SplitMultilineStrings": { "type": "boolean" },
"SortArrays": { "type": "boolean" },
"UsePbixFileNameWhenSavingToDisk": { "type": "boolean" },
"CreateUserOptionsForNewModels": { "type": "boolean" },
"ProxyType": {
"type": "string",
"enum": ["None", "System", "Manual"]
},
"ProxyBypassList": { "type": "string" },
"ProxyUseDefaultCredentials": { "type": "boolean" },
"ProxyAddress": { "type": "string" },
"ProxyBypassOnLocal": { "type": "boolean" },
"ProxyUser": { "type": "string" },
"ProxyPasswordEncrypted": { "type": "string" },
"SaveToFolder_PrefixFiles": { "type": "boolean" },
"SaveToFolder_LocalRelationships": { "type": "boolean" },
"SaveToFolder_LocalPerspectives": { "type": "boolean" },
"SaveToFolder_LocalTranslations": { "type": "boolean" },
"SaveToFolder_UseRecommendedSettings": { "type": "boolean" },
"SaveToFolder_UseTmdl": { "type": "boolean" },
"SaveToFolder_Levels": {
"type": "array",
"description": "Folder structure levels for Save to Folder.",
"items": { "type": "string" }
},
"TmdlOptions": {
"type": "object",
"description": "TMDL serialization options.",
"properties": {
"IncludeRefs": { "type": "boolean" },
"CasingStyle": { "type": "string", "enum": ["CamelCase", "PascalCase", "LowerCase"] },
"ExpressionTrimStyle": { "type": "string" },
"NewLineStyle": { "type": "string", "enum": ["SystemDefault", "Windows", "Unix"] },
"Encoding": { "type": "string", "enum": ["UTF8", "UTF8BOM", "ASCII"] },
"BaseIndentationLevel": { "type": "integer" },
"SpacesIndentation": { "type": "integer" }
}
},
"Copy_IncludeTranslations": { "type": "boolean" },
"Copy_IncludePerspectives": { "type": "boolean" },
"Copy_IncludeRLS": { "type": "boolean" },
"Copy_IncludeOLS": { "type": "boolean" },
"Perspectives_InheritForNewObjects": { "type": "boolean" },
"Perspectives_InheritForRelocatedObjects": { "type": "boolean" },
"Perspectives_InheritTableAdd": { "type": "boolean" },
"Perspectives_InheritTableRemove": { "type": "boolean" },
"View_DisplayFolders": { "type": "boolean" },
"View_HiddenObjects": { "type": "boolean" },
"View_AllObjectTypes": { "type": "boolean" },
"View_SortAlphabetically": { "type": "boolean" },
"View_Measures": { "type": "boolean" },
"View_Columns": { "type": "boolean" },
"View_Hierarchies": { "type": "boolean" },
"View_Partitions": { "type": "boolean" },
"View_MetadataInformation": { "type": "boolean" },
"View_ColumnPreferences": {
"type": "array",
"items": {
"type": "object",
"properties": {
"Name": { "type": "string" },
"Width": { "type": "integer" },
"Visible": { "type": "boolean" }
}
}
},
"BackupOnSave": { "type": "boolean" },
"ShouldBackupOnSave": { "type": "boolean" },
"BackupOnDeploy": { "type": "boolean" },
"ShouldBackupOnDeploy": { "type": "boolean" }
}
}
Tabular Editor 3 Configuration Schemas
Temporary Location: These schemas are stored here temporarily until a dedicated schemas repository is available.
Schemas
| Schema | Validates | Purpose |
|---|---|---|
preferences-schema.json | Preferences.json | Application preferences |
uipreferences-schema.json | UiPreferences.json | UI settings, keyboard shortcuts |
layouts-schema.json | Layouts.json | Window layout configurations |
recentfiles-schema.json | RecentFiles.json | Recent files/models history |
recentservers-schema.json | RecentServers.json | Server connection history |
tmuo-schema.json | *.tmuo | Model-level user options |
Usage
# Using the provided Python script
python scripts/validate_config.py Preferences.json
python scripts/validate_config.py --type tmuo MyModel.JohnDoe.tmuo
# Using ajv-cli
ajv validate -s schema/preferences-schema.json -d Preferences.json
# Using check-jsonschema
check-jsonschema --schemafile schema/tmuo-schema.json Model.Username.tmuoFile Locations
- Application configs:
%LocalAppData%\TabularEditor3\ - TMUO files: Alongside model files as
<ModelFileName>.<WindowsUserName>.tmuo
Important Notes
- TMUO files contain encrypted credentials tied to Windows user accounts - cannot be shared
- Add
*.tmuoto.gitignoreto prevent accidental commits - Application configs (Preferences, UiPreferences, Layouts) can be shared between users
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://github.com/TabularEditor/TabularEditor3/RecentFiles.json",
"title": "Tabular Editor 3 Recent Files",
"description": "Schema for Tabular Editor 3 recent files history. Tracks recently opened scripts, queries, diagrams, pivot grids, and models.",
"$comment": "Version 1.0.0 - Schema location is temporary until schemas repo is available.",
"type": "object",
"properties": {
"RecentFiles": {
"type": "array",
"description": "List of recently opened file paths (scripts, queries, diagrams, etc.).",
"items": {
"type": "string",
"description": "File path to a recently opened file."
}
},
"PinnedFiles": {
"type": "array",
"description": "List of pinned file paths that persist at the top of recent files.",
"items": {
"type": "string"
}
},
"RecentModels": {
"type": "array",
"description": "List of recently opened model paths (.bim, .pbit, .vpax, TMDL folders, etc.).",
"items": {
"type": "string",
"description": "Path to a recently opened model."
}
},
"PinnedModels": {
"type": "array",
"description": "List of pinned model paths that persist at the top of recent models.",
"items": {
"type": "string"
}
}
},
"examples": [
{
"RecentFiles": [
"C:\\Scripts\\format-measures.csx",
"C:\\Queries\\sales-query.dax",
"C:\\Diagrams\\model-diagram.te3diag"
],
"PinnedFiles": [],
"RecentModels": [
"C:\\Models\\SalesModel.bim",
"C:\\Models\\TMDL\\definition\\model.tmdl"
],
"PinnedModels": []
}
]
}
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://github.com/TabularEditor/TabularEditor3/RecentServers.json",
"title": "Tabular Editor 3 Recent Servers",
"description": "Schema for Tabular Editor 3 server connection history. Tracks recently connected Analysis Services, Power BI, and Fabric servers.",
"$comment": "Version 1.0.0 - Schema location is temporary until schemas repo is available.",
"type": "object",
"properties": {
"RecentHistory": {
"type": "array",
"description": "List of recent server connections.",
"items": {
"$ref": "#/definitions/ServerConnection"
}
},
"Recent": {
"$ref": "#/definitions/ServerConnection",
"description": "Most recently used server connection."
}
},
"definitions": {
"ServerConnection": {
"type": "object",
"description": "Server connection configuration.",
"required": ["ServerName"],
"properties": {
"ServerName": {
"type": "string",
"description": "Server connection string (Power BI workspace, Azure AS, SSAS, etc.).",
"examples": [
"powerbi://api.powerbi.com/v1.0/myorg/WorkspaceName",
"asazure://eastus.asazure.windows.net/servername",
"localhost"
]
},
"AuthenticationMode": {
"type": "integer",
"description": "Authentication mode: 0 = Integrated, 1 = Azure AD Interactive, 2 = Service Principal.",
"enum": [0, 1, 2]
},
"Username": {
"type": "string",
"description": "Username for authentication (if applicable)."
},
"Mode": {
"type": "integer",
"description": "Connection mode."
},
"StatusBarColor": {
"type": "string",
"description": "RGBA color for status bar indicator (format: 'A, R, G, B').",
"pattern": "^\\d+,\\s*\\d+,\\s*\\d+,\\s*\\d+$"
}
}
}
},
"examples": [
{
"RecentHistory": [
{
"ServerName": "powerbi://api.powerbi.com/v1.0/myorg/MyWorkspace",
"AuthenticationMode": 1,
"Username": "",
"Mode": 0,
"StatusBarColor": "0, 255, 255, 254"
}
],
"Recent": {
"ServerName": "powerbi://api.powerbi.com/v1.0/myorg/MyWorkspace",
"AuthenticationMode": 1,
"Username": "",
"Mode": 0,
"StatusBarColor": "0, 255, 255, 254"
}
}
]
}
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://docs.tabulareditor.com/references/user-options.html",
"title": "Tabular Editor User Options",
"description": "Schema for Tabular Editor 3 user options file (.tmuo) that stores user-specific settings per model",
"$comment": "Version 1.0.0 - Schema location is temporary until schemas repo is available.",
"type": "object",
"properties": {
"UseWorkspace": {
"type": "boolean",
"description": "Whether to use a workspace database for this model. When true, TE3 deploys metadata to workspace on load."
},
"WorkspaceConnection": {
"description": "Connection string for the workspace database server. Can be plain string or encrypted object.",
"oneOf": [
{
"type": "string",
"examples": [
"localhost",
"provider=MSOLAP;data source=localhost"
]
},
{
"$ref": "#/$defs/encryptedCredential"
}
]
},
"WorkspaceDatabase": {
"type": "string",
"description": "Name of the workspace database. Should be unique per developer and model to avoid conflicts.",
"examples": [
"MyModel_Workspace_JohnDoe",
"AdventureWorks_Dev_20240115"
]
},
"Deployment": {
"type": "object",
"description": "Deployment preferences for this model.",
"properties": {
"TargetConnectionString": {
"description": "Target server connection string for deployment.",
"oneOf": [
{
"type": "string",
"examples": [
"powerbi://api.powerbi.com/v1.0/myorg/MyWorkspace"
]
},
{
"$ref": "#/$defs/encryptedCredential"
}
]
},
"TargetDatabase": {
"type": "string",
"description": "Target database name for deployment.",
"examples": [
"Sales Analytics",
"MyModel"
]
},
"TargetCredentials": {
"type": "object",
"description": "Credentials for the target server.",
"properties": {
"UserName": {
"type": "string"
},
"EncryptedPassword": {
"type": "string"
}
},
"additionalProperties": false
},
"DeployDataSources": {
"type": "boolean",
"description": "Whether to deploy data source definitions.",
"default": false
},
"DeployPartitions": {
"type": "boolean",
"description": "Whether to deploy partition definitions.",
"default": false
},
"DeployRefreshPolicyPartitions": {
"type": "boolean",
"description": "Whether to deploy incremental refresh policy partitions.",
"default": true
},
"DeployModelRoles": {
"type": "boolean",
"description": "Whether to deploy security roles.",
"default": true
},
"DeployModelRoleMembers": {
"type": "boolean",
"description": "Whether to deploy role members.",
"default": false
},
"DeploySharedExpressions": {
"type": "boolean",
"description": "Whether to deploy shared M expressions (named expressions).",
"default": true
}
},
"additionalProperties": false
},
"DataSourceOverrides": {
"type": "object",
"description": "Data source overrides keyed by data source name. Used to override connections for workspace database.",
"additionalProperties": {
"$ref": "#/$defs/dataSourceOverride"
}
},
"TableImportSettings": {
"type": "object",
"description": "Table import settings keyed by table name or data source identifier. Used for Import Tables feature.",
"additionalProperties": {
"$ref": "#/$defs/tableImportSetting"
}
},
"RefreshOverrides": {
"type": "object",
"description": "Advanced refresh override profiles keyed by profile name.",
"additionalProperties": {
"$ref": "#/$defs/refreshOverrideProfile"
}
}
},
"additionalProperties": true,
"$defs": {
"encryptedCredential": {
"type": "object",
"description": "Encrypted credential object using Windows User Key.",
"properties": {
"ConnectionString": {
"type": "string"
},
"EncryptedCredentials": {
"type": "string"
},
"Encryption": {
"type": "string",
"const": "UserKey"
},
"EncryptedString": {
"type": "string"
}
}
},
"encryptedValue": {
"oneOf": [
{
"type": "string"
},
{
"type": "object",
"properties": {
"Encryption": {
"type": "string",
"const": "UserKey"
},
"EncryptedString": {
"type": "string"
}
},
"required": [
"Encryption",
"EncryptedString"
]
}
]
},
"dataSourceOverride": {
"type": "object",
"description": "Override settings for a specific data source.",
"properties": {
"ImpersonationMode": {
"type": "string",
"enum": [
"Default",
"ImpersonateAccount",
"ImpersonateAnonymous",
"ImpersonateCurrentUser",
"ImpersonateServiceAccount",
"ImpersonateUnattendedAccount"
],
"default": "Default"
},
"Username": {
"type": "string"
},
"ConnectionString": {
"$ref": "#/$defs/encryptedValue"
},
"Password": {
"$ref": "#/$defs/encryptedValue"
},
"AccountKey": {
"$ref": "#/$defs/encryptedValue"
},
"PrivacySetting": {
"type": "string"
}
},
"additionalProperties": false
},
"tableImportSetting": {
"type": "object",
"description": "Import settings for a specific table or data source.",
"properties": {
"ServerType": {
"type": "string",
"enum": [
"Sql",
"Oracle",
"Odbc",
"OleDb",
"Snowflake",
"Dataflow",
"PostgreSql",
"MySql",
"MariaDb",
"Db2",
"Databricks",
"OneLake"
],
"description": "Type of database server for native query imports."
},
"UserId": {
"type": "string",
"description": "Username for authentication."
},
"Password": {
"$ref": "#/$defs/encryptedValue",
"description": "Password for authentication."
},
"Server": {
"type": "string",
"description": "Server hostname or address."
},
"Database": {
"type": "string",
"description": "Database name."
},
"Authentication": {
"type": "integer",
"description": "Authentication type (0 = default)."
},
"Options": {
"type": "object",
"description": "Additional server-specific options.",
"additionalProperties": {
"type": "string"
}
}
},
"additionalProperties": false
},
"refreshOverrideProfile": {
"type": "object",
"description": "A refresh override profile containing scope-specific overrides.",
"properties": {
"Overrides": {
"type": "array",
"description": "Collection of override specifications.",
"items": {
"type": "object",
"properties": {
"Scope": {
"type": "object",
"description": "Object scope these overrides apply to.",
"properties": {
"database": {
"type": "string"
},
"table": {
"type": "string"
},
"partition": {
"type": "string"
},
"column": {
"type": "string"
}
}
},
"DataSources": {
"type": "array",
"description": "Data source overrides for this scope."
},
"Partitions": {
"type": "array",
"description": "Partition source overrides."
},
"Columns": {
"type": "array",
"description": "Column source overrides."
},
"Expressions": {
"type": "array",
"description": "Named expression overrides."
}
}
}
}
}
}
},
"examples": [
{
"UseWorkspace": true,
"WorkspaceConnection": "localhost",
"WorkspaceDatabase": "MyModel_Workspace_JohnDoe",
"Deployment": {
"TargetConnectionString": "powerbi://api.powerbi.com/v1.0/myorg/Production",
"TargetDatabase": "Sales Analytics",
"DeployDataSources": false,
"DeployPartitions": false,
"DeployRefreshPolicyPartitions": true,
"DeployModelRoles": true,
"DeployModelRoleMembers": false,
"DeploySharedExpressions": true
},
"DataSourceOverrides": {
"SQL Server DW": {
"ImpersonationMode": "ImpersonateServiceAccount",
"ConnectionString": "Data Source=dev-server;Initial Catalog=DevDW"
}
},
"TableImportSettings": {
"Sales": {
"ServerType": "Sql",
"UserId": "sqladmin",
"Password": {
"Encryption": "UserKey",
"EncryptedString": "..."
}
}
}
}
]
}
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://github.com/TabularEditor/TabularEditor3/UiPreferences.json",
"title": "Tabular Editor 3 UI Preferences",
"description": "Schema for Tabular Editor 3 UI preferences. Stores window settings, editor configurations, keyboard shortcuts, and find/replace options.",
"$comment": "Version 1.0.0 - Schema location is temporary until schemas repo is available.",
"type": "object",
"properties": {
"ShowToolbarsOnFloatingWindows": { "type": "boolean" },
"FindReplaceDialogOpacity": {
"type": "number",
"minimum": 0,
"maximum": 1
},
"UseCompactPaths": { "type": "boolean" },
"RecentFiles": {
"type": "integer",
"description": "Number of recent files to remember."
},
"RecentModels": {
"type": "integer",
"description": "Number of recent models to remember."
},
"RecentServers": {
"type": "integer",
"description": "Number of recent servers to remember."
},
"ShowWhatsNew": { "type": "boolean" },
"HideInactiveToolbars": { "type": "boolean" },
"HideInactiveMenus": { "type": "boolean" },
"HideVersionNumber": { "type": "boolean" },
"HideModelSourceDetails": { "type": "boolean" },
"ShowFullMenus": { "type": "boolean" },
"ShowFullMenusAfterDelay": { "type": "boolean" },
"LargeIcons": { "type": "boolean" },
"ShowScreenTipsInToolbars": { "type": "boolean" },
"ShowShortcutInScreenTips": { "type": "boolean" },
"MenuAnimationType": {
"type": "string",
"enum": ["None", "Fade", "Slide", "System"]
},
"LockMenusAndToolbars": { "type": "boolean" },
"Skin": {
"type": "string",
"description": "UI skin/theme name."
},
"SkinPalette": { "type": "string" },
"Language": {
"type": "string",
"description": "UI language code.",
"examples": ["en-US", "de-DE", "fr-FR", "zh-CN", "ja-JP"]
},
"WorkspaceSerializationVersion": { "type": "integer" },
"RememberWindowBounds": { "type": "boolean" },
"RememberWindowState": { "type": "boolean" },
"MainWindowState": {
"type": "string",
"enum": ["Normal", "Minimized", "Maximized"]
},
"MainWindowRectangle": {
"type": "object",
"properties": {
"X": { "type": "integer" },
"Y": { "type": "integer" },
"W": { "type": "integer" },
"H": { "type": "integer" }
}
},
"DaxscillaPreferences": {
"type": "object",
"description": "DAX editor (Daxscilla) preferences.",
"properties": {
"CodeActions": {
"type": "object",
"properties": {
"Enabled": { "type": "boolean" },
"VariablePrefix": { "type": "string" },
"ExtensionColumnPrefix": { "type": "string" },
"VariableApplyCasing": { "type": "boolean" },
"VariableCasingAfterPrefix": { "type": "string" }
}
},
"UseSqlBiDaxFormatter": { "type": "boolean" },
"DaxFunctionDocumentationUrl": { "type": "string" },
"CodeAssistPreferences": {
"type": "object",
"properties": {
"PreselectFromPastUsage": { "type": "boolean" },
"ShowCalltips": { "type": "boolean" },
"CalltipsMode": { "type": "string" },
"CalltipPosition": { "type": "string" },
"KpiAutoCompleteMode": { "type": "string" },
"AutoCompleteFilterMode": { "type": "string" },
"AutoBraceEnabled": { "type": "boolean" },
"WrapSelection": { "type": "boolean" },
"AutoIndentNewline": { "type": "boolean" },
"AutoFormatOnClose": { "type": "boolean" },
"CorrectObjectCasing": { "type": "boolean" },
"CorrectKeywordFunctionCasing": { "type": "boolean" },
"KeywordsCasing": { "type": "string", "enum": ["Upper", "Lower", "Default"] },
"FunctionsCasing": { "type": "string", "enum": ["Upper", "Lower", "Default"] },
"AutoDaxFormatting": { "type": "boolean" },
"FixQualifiers": { "type": "boolean" },
"AutoFormatEnabled": { "type": "boolean" },
"ShowAutoComplete": { "type": "boolean" },
"AutoCompleteMode": { "type": "string" },
"AutoCompleteHandleDelete": { "type": "boolean" },
"AsyncAutoCompleteParsing": { "type": "boolean" },
"ShowNotRecommendedFunctions": { "type": "boolean" }
}
},
"FormattingOptions": {
"type": "object",
"properties": {
"AlwaysPrefixExtensionColumns": { "type": "boolean" },
"AlwaysQuoteTables": { "type": "boolean" },
"IndentWidth": { "type": "integer" },
"LongFormatMaxChars": { "type": "integer" },
"NewLineAfterFunction": { "type": "boolean" },
"NewLineBeforeOperator": { "type": "boolean" },
"ParenthesisSpacing": { "type": "boolean" },
"RemoveExtraLineBreaks": { "type": "boolean" },
"ShortFormatMaxChars": { "type": "integer" },
"SpaceAfterFunction": { "type": "boolean" },
"UseTabs": { "type": "boolean" }
}
},
"DaxLocale": { "type": "string" },
"UseHyphenComments": { "type": "boolean" },
"UseTabs": { "type": "boolean" },
"DefineObject_IncludeCommentHeader": { "type": "boolean" },
"QueryDefineObject_IncludeCommentHeader": { "type": "boolean" },
"SemanticEngineFeatureOverrides": { "type": "object" },
"ShowWarningUnusedVariable": { "type": "boolean" },
"DaxFormatterDefault": { "type": "string", "enum": ["Short", "Long"] },
"SortItemsInDaxScript": { "type": "boolean" },
"VisibleWhitespace": { "type": "boolean" },
"VisibleLineNumbers": { "type": "boolean" },
"VisibleCodeFolding": { "type": "boolean" },
"IndentationGuides": { "type": "boolean" }
}
},
"SqlEditorPreferences": {
"type": "object",
"description": "SQL editor preferences.",
"properties": {
"VisibleWhitespace": { "type": "boolean" },
"VisibleLineNumbers": { "type": "boolean" },
"VisibleCodeFolding": { "type": "boolean" },
"UseTabs": { "type": "boolean" },
"IndentationGuides": { "type": "boolean" }
}
},
"MEditorPreferences": {
"type": "object",
"description": "M/Power Query editor preferences.",
"properties": {
"VisibleWhitespace": { "type": "boolean" },
"VisibleLineNumbers": { "type": "boolean" },
"VisibleCodeFolding": { "type": "boolean" },
"UseTabs": { "type": "boolean" },
"IndentationGuides": { "type": "boolean" }
}
},
"CSharpEditorPreferences": {
"type": "object",
"description": "C# script editor preferences.",
"properties": {
"VisibleWhitespace": { "type": "boolean" },
"VisibleLineNumbers": { "type": "boolean" },
"VisibleCodeFolding": { "type": "boolean" },
"UseTabs": { "type": "boolean" },
"IndentationGuides": { "type": "boolean" }
}
},
"EditorPreferences": {
"type": "object",
"description": "General editor preferences.",
"properties": {
"MultiPaste": { "type": "boolean" },
"ShowIndicatorsOnScrollBar": { "type": "boolean" },
"WordWrap": { "type": "boolean" },
"WordWrapIndicators": { "type": "string" },
"WordWrapIndicatorEnd": { "type": "boolean" },
"WordWrapIndicatorStart": { "type": "boolean" },
"WordWrapIndicatorMargin": { "type": "boolean" }
}
},
"DebuggerPreferences": {
"type": "object",
"description": "DAX debugger preferences.",
"properties": {
"IncludeTableExpressionsInLocals": { "type": "boolean" },
"IncludeFunctionArgsInLocals": { "type": "boolean" },
"PopupWatchInspector": { "type": "boolean" },
"PopupLocalsInspector": { "type": "boolean" }
}
},
"KeyboardCustomizations": {
"type": "object",
"description": "Custom keyboard shortcuts.",
"properties": {
"Custom": {
"type": "object",
"additionalProperties": {
"oneOf": [
{ "type": "string" },
{ "type": "array", "items": { "type": "string" } }
]
}
}
}
},
"FindReplaceSettings": {
"type": "object",
"description": "Find and replace dialog settings.",
"properties": {
"MatchCase": { "type": "boolean" },
"WholeWord": { "type": "boolean" },
"AllowBackslash": { "type": "boolean" },
"AllowRegex": { "type": "boolean" },
"FindMru": {
"type": "array",
"description": "Most recently used search terms.",
"items": { "type": "string" }
},
"MaxRecentItems": { "type": "integer" }
}
},
"ExplorerSettings": {
"type": "object",
"description": "Model Explorer view settings.",
"properties": {
"ShowInfoColumns": { "type": "boolean" },
"ShowHiddenObjects": { "type": "boolean" },
"ShowMeasures": { "type": "boolean" },
"ShowHierarchies": { "type": "boolean" },
"ShowColumns": { "type": "boolean" },
"ShowPartitions": { "type": "boolean" },
"ShowCalendars": { "type": "boolean" },
"ShowDisplayFolders": { "type": "boolean" },
"ShowNamespaces": { "type": "boolean" },
"ShowEntireBranchWhenFiltering": { "type": "boolean" },
"AlwaysShowDeleteWarning": { "type": "boolean" },
"InfoColumns": { "type": "object" },
"LockColumnWidths": { "type": "boolean" },
"ShowTableGroups": { "type": "boolean" },
"HighlightRelationships": { "type": "boolean" },
"CurrencyFormatSettings": { "type": "object" }
}
},
"PerspectiveEditorSettings": {
"type": "object",
"properties": {
"ShowHiddenObjects": { "type": "boolean" }
}
},
"MetadataTranslationEditorSettings": {
"type": "object",
"properties": {
"ShowHiddenObjects": { "type": "boolean" }
}
},
"OneTimeMessages": {
"type": "object",
"description": "Tracking for one-time notification messages.",
"additionalProperties": { "type": "boolean" }
}
}
}
#!/usr/bin/env python3
"""
Validate Tabular Editor 3 Configuration Files
Validates TE3 configuration files against their JSON schemas:
- Preferences.json
- UiPreferences.json
- Layouts.json
- RecentFiles.json
- RecentServers.json
- *.tmuo (model-level user options)
Usage:
python validate_config.py <file.json>
python validate_config.py --type preferences Preferences.json
python validate_config.py --type tmuo Model.Username.tmuo
python validate_config.py --stdin --type layouts < Layouts.json
Requirements:
pip install jsonschema
"""
#region Imports
import json
import sys
from pathlib import Path
try:
from jsonschema import Draft7Validator
HAS_JSONSCHEMA = True
except ImportError:
HAS_JSONSCHEMA = False
#endregion
#region Variables
SCRIPT_DIR = Path(__file__).parent
SCHEMA_DIR = SCRIPT_DIR.parent / "schema"
CONFIG_TYPES = {
"preferences": "preferences-schema.json",
"uipreferences": "uipreferences-schema.json",
"layouts": "layouts-schema.json",
"recentfiles": "recentfiles-schema.json",
"recentservers": "recentservers-schema.json",
"tmuo": "tmuo-schema.json",
}
FILE_TYPE_MAPPING = {
"preferences.json": "preferences",
"uipreferences.json": "uipreferences",
"layouts.json": "layouts",
"recentfiles.json": "recentfiles",
"recentservers.json": "recentservers",
}
#endregion
#region Functions
def detect_config_type(filename: str) -> str | None:
"""
Detect configuration type from filename.
Args:
filename: Name of the file being validated
Returns:
Config type string or None if undetected
"""
lower_name = filename.lower()
if lower_name.endswith(".tmuo"):
return "tmuo"
for pattern, config_type in FILE_TYPE_MAPPING.items():
if lower_name.endswith(pattern):
return config_type
return None
def load_schema(config_type: str) -> dict | None:
"""
Load the JSON schema for a configuration type.
Args:
config_type: One of the CONFIG_TYPES keys
Returns:
The schema dict if found and valid, None otherwise.
"""
if config_type not in CONFIG_TYPES:
print(f" [ERROR] Unknown config type: {config_type}")
return None
schema_path = SCHEMA_DIR / CONFIG_TYPES[config_type]
if not schema_path.exists():
print(f" [WARN] Schema file not found: {schema_path}")
return None
try:
return json.loads(schema_path.read_text())
except json.JSONDecodeError as e:
print(f" [WARN] Invalid schema JSON: {e}")
return None
def validate_with_schema(data: dict | list, schema: dict) -> list[str]:
"""
Validate data against JSON Schema.
Args:
data: Parsed JSON content
schema: JSON Schema dict
Returns:
List of validation error messages
"""
if not HAS_JSONSCHEMA:
print(" [WARN] jsonschema not installed, skipping schema validation")
print(" Install with: pip install jsonschema")
return []
errors = []
validator = Draft7Validator(schema)
for error in validator.iter_errors(data):
path = " -> ".join(str(p) for p in error.absolute_path) if error.absolute_path else "root"
errors.append(f"Schema: [{path}] {error.message}")
return errors
def validate_tmuo_extras(data: dict) -> list[str]:
"""
Additional validation for TMUO files.
Args:
data: TMUO file content
Returns:
List of error/warning messages
"""
messages = []
ws_db = data.get("WorkspaceDatabase", "")
if ws_db and not any(char in ws_db for char in ["_", "-"]):
print(" [WARN] WorkspaceDatabase name should include user identifier to avoid conflicts")
def check_for_plaintext(obj: dict, path: str = ""):
for key, value in obj.items():
current_path = f"{path}.{key}" if path else key
if key.lower() in ["password", "accountkey"] and isinstance(value, str) and value:
print(f" [WARN] [{current_path}] Contains plain-text credential")
elif isinstance(value, dict):
check_for_plaintext(value, current_path)
check_for_plaintext(data)
deployment = data.get("Deployment", {})
if deployment:
target = deployment.get("TargetConnectionString", "")
if isinstance(target, str) and "powerbi://" in target.lower():
if deployment.get("DeployPartitions", False):
print(" [WARN] DeployPartitions=true may cause issues with Power BI Service")
if data.get("UseWorkspace") and not data.get("WorkspaceConnection"):
messages.append("UseWorkspace is true but WorkspaceConnection is not set")
return messages
def validate_preferences_extras(data: dict) -> list[str]:
"""
Additional validation for Preferences.json.
Args:
data: Preferences file content
Returns:
List of error/warning messages
"""
messages = []
if data.get("ProxyType") == "Manual" and not data.get("ProxyAddress"):
messages.append("ProxyType is Manual but ProxyAddress is not set")
if data.get("BackupOnSave") and not data.get("SaveBackupLocation"):
print(" [WARN] BackupOnSave is true but SaveBackupLocation is not set")
return messages
def validate_config_file(
data: dict | list,
config_type: str,
schema: dict | None,
schema_only: bool = False
) -> tuple[int, list[str]]:
"""
Validate configuration file content.
Args:
data: Parsed JSON content
config_type: Type of configuration file
schema: JSON Schema dict (optional)
schema_only: If True, only run schema validation
Returns:
Tuple of (item_count, error_messages)
"""
all_errors = []
if schema:
schema_errors = validate_with_schema(data, schema)
all_errors.extend(schema_errors)
if schema_only:
return len(data) if isinstance(data, (dict, list)) else 0, all_errors
if config_type == "tmuo" and isinstance(data, dict):
extra_errors = validate_tmuo_extras(data)
all_errors.extend(extra_errors)
elif config_type == "preferences" and isinstance(data, dict):
extra_errors = validate_preferences_extras(data)
all_errors.extend(extra_errors)
return len(data) if isinstance(data, (dict, list)) else 0, all_errors
def main():
"""
Main entry point for configuration validation.
"""
if len(sys.argv) < 2:
print("Usage: python validate_config.py <file.json>")
print(" python validate_config.py --type <type> <file.json>")
print(" python validate_config.py --stdin --type <type>")
print()
print("Types: " + ", ".join(CONFIG_TYPES.keys()))
sys.exit(1)
schema_only = "--schema-only" in sys.argv
args = [a for a in sys.argv[1:] if a != "--schema-only"]
config_type = None
if "--type" in args:
type_idx = args.index("--type")
if type_idx + 1 < len(args):
config_type = args[type_idx + 1].lower()
args = args[:type_idx] + args[type_idx + 2:]
else:
print("Error: --type requires a value")
sys.exit(1)
if not args:
print("Error: No input file specified")
sys.exit(1)
if args[0] == "--stdin":
content = sys.stdin.read()
source = "stdin"
if not config_type:
print("Error: --type is required when using --stdin")
sys.exit(1)
else:
file_path = Path(args[0])
if not file_path.exists():
print(f"Error: File not found: {file_path}")
sys.exit(1)
content = file_path.read_text()
source = str(file_path)
if not config_type:
config_type = detect_config_type(file_path.name)
if not config_type:
print(f"Error: Could not detect config type from filename '{file_path.name}'")
print(" Use --type to specify: " + ", ".join(CONFIG_TYPES.keys()))
sys.exit(1)
try:
data = json.loads(content)
except json.JSONDecodeError as e:
print(f"Error: Invalid JSON - {e}")
sys.exit(1)
schema = load_schema(config_type)
print(f"Validating {config_type} config: {source}...")
if schema:
print(f"Using schema: {SCHEMA_DIR / CONFIG_TYPES[config_type]}")
item_count, errors = validate_config_file(data, config_type, schema, schema_only)
print()
if errors:
print(f"Found {len(errors)} error(s):")
for error in errors:
print(f" [ERROR] {error}")
sys.exit(1)
else:
print(f"Config file is valid ({item_count} top-level items).")
sys.exit(0)
#endregion
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Validate Tabular Editor User Options (.tmuo) files
Validates .tmuo files against the JSON schema and checks for
common issues and best practices.
Usage:
python validate_tmuo.py <file.tmuo>
python validate_tmuo.py --stdin < file.tmuo
python validate_tmuo.py --schema-only <file.tmuo>
Requirements:
pip install jsonschema
"""
#region Imports
import json
import sys
from pathlib import Path
try:
from jsonschema import Draft7Validator
HAS_JSONSCHEMA = True
except ImportError:
HAS_JSONSCHEMA = False
#endregion
#region Variables
SCRIPT_DIR = Path(__file__).parent
SCHEMA_PATH = SCRIPT_DIR.parent / "schema" / "tmuo-schema.json"
VALID_IMPERSONATION_MODES = [
"Default", "ImpersonateAccount", "ImpersonateAnonymous",
"ImpersonateCurrentUser", "ImpersonateServiceAccount",
"ImpersonateUnattendedAccount"
]
VALID_SERVER_TYPES = [
"Sql", "Oracle", "Odbc", "OleDb", "Snowflake", "Dataflow",
"PostgreSql", "MySql", "MariaDb", "Db2", "Databricks", "OneLake"
]
#endregion
#region Functions
def load_schema() -> dict | None:
"""
Load the TMUO JSON schema from the schema directory.
Returns:
The schema dict if found and valid, None otherwise.
"""
if not SCHEMA_PATH.exists():
print(f" [WARN] Schema file not found: {SCHEMA_PATH}")
return None
try:
return json.loads(SCHEMA_PATH.read_text())
except json.JSONDecodeError as e:
print(f" [WARN] Invalid schema JSON: {e}")
return None
def validate_with_schema(data: dict, schema: dict) -> list[str]:
"""
Validate TMUO against JSON Schema.
Args:
data: TMUO file content
schema: JSON Schema dict
Returns:
List of validation error messages
"""
if not HAS_JSONSCHEMA:
print(" [WARN] jsonschema not installed, skipping schema validation")
print(" Install with: pip install jsonschema")
return []
errors = []
validator = Draft7Validator(schema)
for error in validator.iter_errors(data):
path = " -> ".join(str(p) for p in error.absolute_path) if error.absolute_path else "root"
errors.append(f"Schema: [{path}] {error.message}")
return errors
def validate_tmuo_extras(data: dict) -> list[str]:
"""
Additional validation beyond JSON Schema.
Args:
data: TMUO file content
Returns:
List of error/warning messages
"""
messages = []
# Check workspace database naming
ws_db = data.get("WorkspaceDatabase", "")
if ws_db and not any(char in ws_db for char in ["_", "-"]):
print(" [WARN] WorkspaceDatabase name should include user identifier to avoid conflicts")
# Check for plain-text passwords (security warning)
def check_for_plaintext(obj: dict, path: str = ""):
for key, value in obj.items():
current_path = f"{path}.{key}" if path else key
if key.lower() in ["password", "accountkey"] and isinstance(value, str) and value:
print(f" [WARN] [{current_path}] Contains plain-text credential - consider using encrypted format")
elif isinstance(value, dict):
check_for_plaintext(value, current_path)
check_for_plaintext(data)
# Check deployment settings
deployment = data.get("Deployment", {})
if deployment:
target = deployment.get("TargetConnectionString", "")
if isinstance(target, str) and "powerbi://" in target.lower():
if deployment.get("DeployPartitions", False):
print(" [WARN] DeployPartitions=true may cause issues with Power BI Service")
if deployment.get("DeployDataSources", False):
print(" [WARN] DeployDataSources=true is typically not needed for Power BI Service")
# Check for UseWorkspace without connection
if data.get("UseWorkspace") and not data.get("WorkspaceConnection"):
messages.append("UseWorkspace is true but WorkspaceConnection is not set")
return messages
def validate_tmuo_file(data: dict, schema: dict | None, schema_only: bool = False) -> tuple[int, list[str]]:
"""
Validate TMUO file content.
Args:
data: Parsed JSON content
schema: JSON Schema dict (optional)
schema_only: If True, only run schema validation
Returns:
Tuple of (section_count, error_messages)
"""
all_errors = []
# Check basic structure
if not isinstance(data, dict):
return 0, ["File must be a JSON object"]
# JSON Schema validation
if schema:
schema_errors = validate_with_schema(data, schema)
all_errors.extend(schema_errors)
if schema_only:
return len(data), all_errors
# Additional validation
extra_errors = validate_tmuo_extras(data)
all_errors.extend(extra_errors)
return len(data), all_errors
def main():
"""
Main entry point for TMUO validation.
"""
if len(sys.argv) < 2:
print("Usage: python validate_tmuo.py <file.tmuo>")
print(" python validate_tmuo.py --stdin < file.tmuo")
print(" python validate_tmuo.py --schema-only <file.tmuo>")
sys.exit(1)
schema_only = "--schema-only" in sys.argv
args = [a for a in sys.argv[1:] if a != "--schema-only"]
if not args:
print("Error: No input file specified")
sys.exit(1)
# Read input
if args[0] == "--stdin":
content = sys.stdin.read()
source = "stdin"
else:
file_path = Path(args[0])
if not file_path.exists():
print(f"Error: File not found: {file_path}")
sys.exit(1)
content = file_path.read_text()
source = str(file_path)
# Parse JSON
try:
data = json.loads(content)
except json.JSONDecodeError as e:
print(f"Error: Invalid JSON - {e}")
sys.exit(1)
# Load schema
schema = load_schema()
# Validate
print(f"Validating TMUO file: {source}...")
if schema:
print(f"Using schema: {SCHEMA_PATH}")
section_count, errors = validate_tmuo_file(data, schema, schema_only)
# Output results
print()
if errors:
print(f"Found {len(errors)} error(s):")
for error in errors:
print(f" [ERROR] {error}")
sys.exit(1)
else:
print(f"TMUO file is valid ({section_count} top-level settings).")
sys.exit(0)
#endregion
if __name__ == "__main__":
main()