├── .gitignore ├── CODE_OF_CONDUCT.md ├── DTDLValidator-Sample ├── .gitignore ├── .vscode │ ├── launch.json │ └── tasks.json ├── DTDLValidator.sln └── DTDLValidator │ ├── DTDLValidator.csproj │ ├── GlobalUsings.cs │ ├── Interactive │ ├── CompareCommand.cs │ ├── DTDLParser.cs │ ├── ExitCommand.cs │ ├── Interactive.cs │ ├── ListCommand.cs │ ├── LoadCommand.cs │ ├── ShowCommand.cs │ └── ShowInfoCommand.cs │ ├── ListExtensions.cs │ ├── Log.cs │ └── Program.cs ├── LICENSE ├── README.md └── SECURITY.md /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | .vscode/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # StyleCop 66 | StyleCopReport.xml 67 | 68 | # Files built by Visual Studio 69 | *_i.c 70 | *_p.c 71 | *_h.h 72 | *.ilk 73 | *.meta 74 | *.obj 75 | *.iobj 76 | *.pch 77 | *.pdb 78 | *.ipdb 79 | *.pgc 80 | *.pgd 81 | *.rsp 82 | *.sbr 83 | *.tlb 84 | *.tli 85 | *.tlh 86 | *.tmp 87 | *.tmp_proj 88 | *_wpftmp.csproj 89 | *.log 90 | *.vspscc 91 | *.vssscc 92 | .builds 93 | *.pidb 94 | *.svclog 95 | *.scc 96 | 97 | # Chutzpah Test files 98 | _Chutzpah* 99 | 100 | # Visual C++ cache files 101 | ipch/ 102 | *.aps 103 | *.ncb 104 | *.opendb 105 | *.opensdf 106 | *.sdf 107 | *.cachefile 108 | *.VC.db 109 | *.VC.VC.opendb 110 | 111 | # Visual Studio profiler 112 | *.psess 113 | *.vsp 114 | *.vspx 115 | *.sap 116 | 117 | # Visual Studio Trace Files 118 | *.e2e 119 | 120 | # TFS 2012 Local Workspace 121 | $tf/ 122 | 123 | # Guidance Automation Toolkit 124 | *.gpState 125 | 126 | # ReSharper is a .NET coding add-in 127 | _ReSharper*/ 128 | *.[Rr]e[Ss]harper 129 | *.DotSettings.user 130 | 131 | # TeamCity is a build add-in 132 | _TeamCity* 133 | 134 | # DotCover is a Code Coverage Tool 135 | *.dotCover 136 | 137 | # AxoCover is a Code Coverage Tool 138 | .axoCover/* 139 | !.axoCover/settings.json 140 | 141 | # Visual Studio code coverage results 142 | *.coverage 143 | *.coveragexml 144 | 145 | # NCrunch 146 | _NCrunch_* 147 | .*crunch*.local.xml 148 | nCrunchTemp_* 149 | 150 | # MightyMoose 151 | *.mm.* 152 | AutoTest.Net/ 153 | 154 | # Web workbench (sass) 155 | .sass-cache/ 156 | 157 | # Installshield output folder 158 | [Ee]xpress/ 159 | 160 | # DocProject is a documentation generator add-in 161 | DocProject/buildhelp/ 162 | DocProject/Help/*.HxT 163 | DocProject/Help/*.HxC 164 | DocProject/Help/*.hhc 165 | DocProject/Help/*.hhk 166 | DocProject/Help/*.hhp 167 | DocProject/Help/Html2 168 | DocProject/Help/html 169 | 170 | # Click-Once directory 171 | publish/ 172 | 173 | # Publish Web Output 174 | *.[Pp]ublish.xml 175 | *.azurePubxml 176 | # Note: Comment the next line if you want to checkin your web deploy settings, 177 | # but database connection strings (with potential passwords) will be unencrypted 178 | *.pubxml 179 | *.publishproj 180 | 181 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 182 | # checkin your Azure Web App publish settings, but sensitive information contained 183 | # in these scripts will be unencrypted 184 | PublishScripts/ 185 | 186 | # NuGet Packages 187 | *.nupkg 188 | # NuGet Symbol Packages 189 | *.snupkg 190 | # The packages folder can be ignored because of Package Restore 191 | **/[Pp]ackages/* 192 | # except build/, which is used as an MSBuild target. 193 | !**/[Pp]ackages/build/ 194 | # Uncomment if necessary however generally it will be regenerated when needed 195 | #!**/[Pp]ackages/repositories.config 196 | # NuGet v3's project.json files produces more ignorable files 197 | *.nuget.props 198 | *.nuget.targets 199 | 200 | # Microsoft Azure Build Output 201 | csx/ 202 | *.build.csdef 203 | 204 | # Microsoft Azure Emulator 205 | ecf/ 206 | rcf/ 207 | 208 | # Windows Store app package directories and files 209 | AppPackages/ 210 | BundleArtifacts/ 211 | Package.StoreAssociation.xml 212 | _pkginfo.txt 213 | *.appx 214 | *.appxbundle 215 | *.appxupload 216 | 217 | # Visual Studio cache files 218 | # files ending in .cache can be ignored 219 | *.[Cc]ache 220 | # but keep track of directories ending in .cache 221 | !?*.[Cc]ache/ 222 | 223 | # Others 224 | ClientBin/ 225 | ~$* 226 | *~ 227 | *.dbmdl 228 | *.dbproj.schemaview 229 | *.jfm 230 | *.pfx 231 | *.publishsettings 232 | orleans.codegen.cs 233 | 234 | # Including strong name files can present a security risk 235 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 236 | #*.snk 237 | 238 | # Since there are multiple workflows, uncomment next line to ignore bower_components 239 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 240 | #bower_components/ 241 | 242 | # RIA/Silverlight projects 243 | Generated_Code/ 244 | 245 | # Backup & report files from converting an old project file 246 | # to a newer Visual Studio version. Backup files are not needed, 247 | # because we have git ;-) 248 | _UpgradeReport_Files/ 249 | Backup*/ 250 | UpgradeLog*.XML 251 | UpgradeLog*.htm 252 | ServiceFabricBackup/ 253 | *.rptproj.bak 254 | 255 | # SQL Server files 256 | *.mdf 257 | *.ldf 258 | *.ndf 259 | 260 | # Business Intelligence projects 261 | *.rdl.data 262 | *.bim.layout 263 | *.bim_*.settings 264 | *.rptproj.rsuser 265 | *- [Bb]ackup.rdl 266 | *- [Bb]ackup ([0-9]).rdl 267 | *- [Bb]ackup ([0-9][0-9]).rdl 268 | 269 | # Microsoft Fakes 270 | FakesAssemblies/ 271 | 272 | # GhostDoc plugin setting file 273 | *.GhostDoc.xml 274 | 275 | # Node.js Tools for Visual Studio 276 | .ntvs_analysis.dat 277 | node_modules/ 278 | 279 | # Visual Studio 6 build log 280 | *.plg 281 | 282 | # Visual Studio 6 workspace options file 283 | *.opt 284 | 285 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 286 | *.vbw 287 | 288 | # Visual Studio LightSwitch build output 289 | **/*.HTMLClient/GeneratedArtifacts 290 | **/*.DesktopClient/GeneratedArtifacts 291 | **/*.DesktopClient/ModelManifest.xml 292 | **/*.Server/GeneratedArtifacts 293 | **/*.Server/ModelManifest.xml 294 | _Pvt_Extensions 295 | 296 | # Paket dependency manager 297 | .paket/paket.exe 298 | paket-files/ 299 | 300 | # FAKE - F# Make 301 | .fake/ 302 | 303 | # CodeRush personal settings 304 | .cr/personal 305 | 306 | # Python Tools for Visual Studio (PTVS) 307 | __pycache__/ 308 | *.pyc 309 | 310 | # Cake - Uncomment if you are using it 311 | # tools/** 312 | # !tools/packages.config 313 | 314 | # Tabs Studio 315 | *.tss 316 | 317 | # Telerik's JustMock configuration file 318 | *.jmconfig 319 | 320 | # BizTalk build output 321 | *.btp.cs 322 | *.btm.cs 323 | *.odx.cs 324 | *.xsd.cs 325 | 326 | # OpenCover UI analysis results 327 | OpenCover/ 328 | 329 | # Azure Stream Analytics local run output 330 | ASALocalRun/ 331 | 332 | # MSBuild Binary and Structured Log 333 | *.binlog 334 | 335 | # NVidia Nsight GPU debugger configuration file 336 | *.nvuser 337 | 338 | # MFractors (Xamarin productivity tool) working folder 339 | .mfractor/ 340 | 341 | # Local History for Visual Studio 342 | .localhistory/ 343 | 344 | # BeatPulse healthcheck temp database 345 | healthchecksdb 346 | 347 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 348 | MigrationBackup/ 349 | 350 | # Ionide (cross platform F# VS Code tools) working folder 351 | .ionide/ 352 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /DTDLValidator-Sample/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | -------------------------------------------------------------------------------- /DTDLValidator-Sample/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/DTDLValidator/bin/Debug/netcoreapp3.1/DTDLValidator.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}/DTDLValidator", 16 | // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console 17 | "console": "internalConsole", 18 | "stopAtEntry": false 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach", 24 | "processId": "${command:pickProcess}" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /DTDLValidator-Sample/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/DTDLValidator/DTDLValidator.csproj", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "publish", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "publish", 22 | "${workspaceFolder}/DTDLValidator/DTDLValidator.csproj", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "watch", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "watch", 34 | "run", 35 | "${workspaceFolder}/DTDLValidator/DTDLValidator.csproj", 36 | "/property:GenerateFullPaths=true", 37 | "/consoleloggerparameters:NoSummary" 38 | ], 39 | "problemMatcher": "$msCompile" 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /DTDLValidator-Sample/DTDLValidator.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30011.22 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DTDLValidator", "DTDLValidator\DTDLValidator.csproj", "{97A2EE2B-0FB4-4D61-A7BC-8A2FC166425E}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {97A2EE2B-0FB4-4D61-A7BC-8A2FC166425E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {97A2EE2B-0FB4-4D61-A7BC-8A2FC166425E}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {97A2EE2B-0FB4-4D61-A7BC-8A2FC166425E}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {97A2EE2B-0FB4-4D61-A7BC-8A2FC166425E}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {29693B31-2D04-4C59-99AD-12F65B833EF6} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /DTDLValidator-Sample/DTDLValidator/DTDLValidator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /DTDLValidator-Sample/DTDLValidator/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using DTDLParser; 2 | global using DTDLParser.Models; 3 | -------------------------------------------------------------------------------- /DTDLValidator-Sample/DTDLValidator/Interactive/CompareCommand.cs: -------------------------------------------------------------------------------- 1 | namespace DTDLValidator.Interactive 2 | { 3 | using CommandLine; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | 9 | [Verb("compare", HelpText = "Compare two models.")] 10 | internal class CompareCommand 11 | { 12 | 13 | [Value(0, HelpText = "First model id to compare.")] 14 | public string FirstModelId { get; set; } 15 | 16 | [Value(1, HelpText = "Second model id to compare.")] 17 | public string SecondModelId { get; set; } 18 | 19 | public Task Run(Interactive p) 20 | { 21 | if (FirstModelId == null || SecondModelId == null) 22 | { 23 | Log.Error("Please specify two valid model ids as parameters"); 24 | return Task.FromResult(null); 25 | } 26 | 27 | bool firstValid = false; 28 | bool secondValid = false; 29 | 30 | Dtmi first = ValidateAndCreateDtmi(FirstModelId); 31 | Dtmi second = ValidateAndCreateDtmi(SecondModelId); 32 | DTInterfaceInfo dt1 = null; 33 | DTInterfaceInfo dt2 = null; 34 | 35 | if (first != null && p.Models.TryGetValue(first, out dt1)) 36 | firstValid = true; 37 | if (second != null && p.Models.TryGetValue(second, out dt2)) 38 | secondValid = true; 39 | 40 | if (firstValid == false || secondValid == false) 41 | { 42 | if (first == null) 43 | Log.Error($"First model not a valid dtmi"); 44 | if (first != null && firstValid == false) 45 | Log.Error($"First model not found in loaded models"); 46 | if (second == null) 47 | Log.Error($"Second model not a valid dtmi"); 48 | if (second != null && secondValid == false) 49 | Log.Error($"Second model not found in loaded models"); 50 | return Task.FromResult(null); 51 | } 52 | 53 | IReadOnlyDictionary con1 = dt1.Contents; 54 | IReadOnlyDictionary con2 = dt2.Contents; 55 | 56 | var props1 = con1 57 | .Where(p => p.Value.EntityKind == DTEntityKind.Property) 58 | .Select(p => p.Value as DTPropertyInfo); 59 | 60 | var props2 = con2 61 | .Where(p => p.Value.EntityKind == DTEntityKind.Property) 62 | .Select(p => p.Value as DTPropertyInfo); 63 | 64 | IEnumerable duplicates = props1.Intersect(props2, new DTPropertyInfoComparer()); 65 | IEnumerable diff1 = props1.Except(props2, new DTPropertyInfoComparer()); 66 | IEnumerable diff2 = props2.Except(props1, new DTPropertyInfoComparer()); 67 | 68 | Log.Alert("Common Properties (comparing name and schema, ignoring explicit ids)"); 69 | Console.WriteLine(listFormatBoth, "Property Name", "Schema"); 70 | Console.WriteLine(listFormatBoth, "-------------", "------"); 71 | foreach (var pi in duplicates) 72 | Console.WriteLine(listFormatBoth, pi.Name, pi.Schema); 73 | 74 | Console.WriteLine(); 75 | PrintDifference(dt1, diff1); 76 | Console.WriteLine(); 77 | PrintDifference(dt2, diff2); 78 | 79 | var rels1 = con1 80 | .Where(p => p.Value.EntityKind == DTEntityKind.Relationship) 81 | .Select(p => p.Value as DTRelationshipInfo); 82 | 83 | var rels2 = con2 84 | .Where(p => p.Value.EntityKind == DTEntityKind.Relationship) 85 | .Select(p => p.Value as DTRelationshipInfo); 86 | 87 | IEnumerable dupRels = rels1.Intersect(rels2, new DTRelationshipInfoComparer()); 88 | IEnumerable diffRels1 = rels1.Except(rels2, new DTRelationshipInfoComparer()); 89 | IEnumerable diffRels2 = rels2.Except(rels1, new DTRelationshipInfoComparer()); 90 | 91 | Console.WriteLine(); 92 | Log.Alert("Common Relationships (comparing name and target - not checking properties, ignoring explicit ids)"); 93 | Console.WriteLine(listFormatBoth, "Relationship Name", "Target"); 94 | Console.WriteLine(listFormatBoth, "-----------------", "------"); 95 | foreach (var pi in dupRels) 96 | { 97 | string target = ""; 98 | if (pi.Target != null) 99 | target = pi.Target.ToString(); 100 | Console.WriteLine(listFormatBoth, pi.Name, target); 101 | } 102 | 103 | Console.WriteLine(); 104 | PrintDifference(dt1, diffRels1); 105 | Console.WriteLine(); 106 | PrintDifference(dt2, diffRels2); 107 | 108 | return Task.FromResult(null); 109 | } 110 | 111 | private const string listFormatBoth = "{0,-30}{1}"; 112 | 113 | private void PrintDifference(DTInterfaceInfo dti, IEnumerable diffs) 114 | { 115 | Log.Alert($"Only in {dti.DisplayName.FirstOrDefault().Value ?? dti.Id.ToString()}"); 116 | Console.WriteLine(listFormatBoth, "Property Name", "Schema"); 117 | Console.WriteLine(listFormatBoth, "-------------", "------"); 118 | foreach (var pi in diffs) 119 | { 120 | Console.WriteLine(listFormatBoth, pi.Name, pi.Schema); 121 | } 122 | } 123 | 124 | private void PrintDifference(DTInterfaceInfo dti, IEnumerable diffs) 125 | { 126 | Log.Alert($"Only in {dti.DisplayName.FirstOrDefault().Value ?? dti.Id.ToString()}"); 127 | Console.WriteLine(listFormatBoth, "Relationship Name", "Schema"); 128 | Console.WriteLine(listFormatBoth, "-----------------", "------"); 129 | foreach (var pi in diffs) 130 | { 131 | Console.WriteLine(listFormatBoth, pi.Name, pi.Target); 132 | } 133 | } 134 | 135 | private Dtmi ValidateAndCreateDtmi(string dtmi) 136 | { 137 | try 138 | { 139 | Dtmi dt = new Dtmi(dtmi); 140 | return dt; 141 | } 142 | catch (Exception) 143 | { 144 | return null; 145 | } 146 | } 147 | } 148 | 149 | // This assumes that the DTProperties are *not* defined with explicit ids 150 | class DTPropertyInfoComparer : IEqualityComparer 151 | { 152 | // Products are equal if their names and product numbers are equal. 153 | public bool Equals(DTPropertyInfo x, DTPropertyInfo y) 154 | { 155 | 156 | //Check whether the compared objects reference the same data. 157 | if (Object.ReferenceEquals(x, y)) return true; 158 | 159 | //Check whether any of the compared objects is null. 160 | if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null)) 161 | return false; 162 | 163 | //Check whether the products' properties are equal. 164 | return x.Name == y.Name && x.Schema == y.Schema; 165 | } 166 | 167 | // If Equals() returns true for a pair of objects 168 | // then GetHashCode() must return the same value for these objects. 169 | 170 | public int GetHashCode(DTPropertyInfo pi) 171 | { 172 | //Check whether the object is null 173 | if (Object.ReferenceEquals(pi, null)) return 0; 174 | 175 | //Get hash code for the Name field if it is not null. 176 | int hashPIName = pi.Name == null ? 0 : pi.Name.GetHashCode(); 177 | 178 | //Get hash code for the Code field. 179 | int hashPISchema = pi.Schema.GetHashCode(); 180 | 181 | //Calculate the hash code for the product. 182 | return hashPIName ^ hashPISchema; 183 | } 184 | } 185 | 186 | // This assumes that the DTRelationships are *not* defined with explicit ids 187 | // Only compares name and target in this sample 188 | class DTRelationshipInfoComparer : IEqualityComparer 189 | { 190 | // Products are equal if their names and product numbers are equal. 191 | public bool Equals(DTRelationshipInfo x, DTRelationshipInfo y) 192 | { 193 | 194 | //Check whether the compared objects reference the same data. 195 | if (Object.ReferenceEquals(x, y)) return true; 196 | 197 | //Check whether any of the compared objects is null. 198 | if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null)) 199 | return false; 200 | 201 | //Check whether the products' properties are equal. 202 | return x.Name == y.Name && x.Target == y.Target; 203 | } 204 | 205 | // If Equals() returns true for a pair of objects 206 | // then GetHashCode() must return the same value for these objects. 207 | 208 | public int GetHashCode(DTRelationshipInfo pi) 209 | { 210 | //Check whether the object is null 211 | if (Object.ReferenceEquals(pi, null)) return 0; 212 | 213 | //Get hash code for the Name field if it is not null. 214 | int hashPIName = pi.Name == null ? 0 : pi.Name.GetHashCode(); 215 | 216 | //Get hash code for the Code field. 217 | if (pi.Target == null) 218 | return hashPIName; 219 | 220 | int hashPITarget = pi.Target.GetHashCode(); 221 | 222 | //Calculate the hash code for the product. 223 | return hashPIName ^ hashPITarget; 224 | } 225 | } 226 | 227 | } 228 | -------------------------------------------------------------------------------- /DTDLValidator-Sample/DTDLValidator/Interactive/DTDLParser.cs: -------------------------------------------------------------------------------- 1 | using DTDLParser; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace DTDLValidator.Interactive 9 | { 10 | internal class DTDLParser 11 | { 12 | private readonly IDictionary modelStore; 13 | public DTDLParser(IDictionary modelStore) 14 | { 15 | this.modelStore = modelStore; 16 | } 17 | 18 | public async Task<(IReadOnlyDictionary, IEnumerable)> ParseAsync(IAsyncEnumerable jsonTexts) 19 | { 20 | // Create resolver state per call to ParseAsync so that multiple calls to ParseAsync can run concurrently. 21 | DTDLResolver dtdlResolver = new DTDLResolver(modelStore); 22 | ModelParser parser = new ModelParser(new ParsingOptions { DtmiResolverAsync = dtdlResolver.Resolver}); 23 | IReadOnlyDictionary entities = await parser.ParseAsync(jsonTexts); 24 | return (entities, dtdlResolver.ResolvedInterfaces); 25 | } 26 | 27 | 28 | private class DTDLResolver 29 | { 30 | private readonly IDictionary modelStore; 31 | public DTDLResolver(IDictionary modelStore) 32 | { 33 | this.modelStore = modelStore; 34 | } 35 | 36 | 37 | #pragma warning disable CS8425 // Async-iterator member has one or more parameters of type 'CancellationToken' but none of them is decorated with the 'EnumeratorCancellation' attribute, so the cancellation token parameter from the generated 'IAsyncEnumerable<>.GetAsyncEnumerator' will be unconsumed 38 | public async IAsyncEnumerable Resolver(IReadOnlyCollection dtmis, CancellationToken cancellationToken) 39 | #pragma warning restore CS8425 // Async-iterator member has one or more parameters of type 'CancellationToken' but none of them is decorated with the 'EnumeratorCancellation' attribute, so the cancellation token parameter from the generated 'IAsyncEnumerable<>.GetAsyncEnumerator' will be unconsumed 40 | { 41 | List texts = new List(); 42 | foreach (Dtmi dtmi in dtmis) 43 | { 44 | if (modelStore.TryGetValue(dtmi, out DTInterfaceInfo @interface)) 45 | { 46 | ResolvedInterfaces.Add(@interface); 47 | texts.Add(@interface.GetJsonLdText()); 48 | yield return @interface.GetJsonLdText(); 49 | } 50 | } 51 | await Task.Yield(); 52 | //return texts; 53 | } 54 | 55 | public IList ResolvedInterfaces { get; private set; } = new List(); 56 | 57 | } 58 | 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /DTDLValidator-Sample/DTDLValidator/Interactive/ExitCommand.cs: -------------------------------------------------------------------------------- 1 | namespace DTDLValidator.Interactive 2 | { 3 | using CommandLine; 4 | 5 | [Verb("exit", HelpText = "Exit CLI.")] 6 | internal class ExitCommand 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /DTDLValidator-Sample/DTDLValidator/Interactive/Interactive.cs: -------------------------------------------------------------------------------- 1 | using CommandLine; 2 | using DTDLParser; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | using System.Linq; 7 | 8 | namespace DTDLValidator.Interactive 9 | { 10 | class Interactive 11 | { 12 | public Interactive() 13 | { 14 | DTDLParser = new DTDLParser(Models); 15 | Task.WaitAll(Run()); 16 | } 17 | 18 | public IDictionary Models { get; private set; } = new Dictionary(); 19 | 20 | public DTDLParser DTDLParser { get; private set; } 21 | 22 | private async Task Run() 23 | { 24 | Console.WriteLine("DTDLValidator Interactive Mode"); 25 | bool exit = false; 26 | while (!exit) 27 | { 28 | Console.WriteLine(); 29 | Console.Write("> "); 30 | string commandLine = Console.ReadLine(); 31 | Task commandTask = Task.FromResult(null); 32 | Parser.Default.ParseArguments< 33 | CompareCommand, 34 | ListCommand, 35 | LoadCommand, 36 | ShowCommand, 37 | ShowInfoCommand, 38 | ExitCommand>(SplitArgs(commandLine)) 39 | .WithParsed(command => commandTask = command.Run(this)) 40 | .WithParsed(command => commandTask = command.Run(this)) 41 | .WithParsed(command => commandTask = command.Run(this)) 42 | .WithParsed(command => commandTask = command.Run(this)) 43 | .WithParsed(command => commandTask = command.Run(this)) 44 | .WithParsed(command => exit = true); 45 | await commandTask; 46 | } 47 | } 48 | 49 | private string[] SplitArgs(string arg) 50 | { 51 | int quotecount = arg.Count(x => x == '"'); 52 | if (quotecount % 2 != 0) 53 | { 54 | Log.Alert("Your command contains an uneven number of quotes. Was that intended?"); 55 | } 56 | string[] segments = arg.Split('"', StringSplitOptions.RemoveEmptyEntries); 57 | List elements = new List(); 58 | for (int i = 0; i < segments.Length; i++) 59 | { 60 | if (i % 2 == 0) 61 | { 62 | string[] parts = segments[i].Split(new char[] { }, StringSplitOptions.RemoveEmptyEntries); 63 | foreach (string ps in parts) 64 | elements.Add(ps.Trim()); 65 | } 66 | else 67 | { 68 | elements.Add(segments[i].Trim()); 69 | } 70 | } 71 | return elements.ToArray(); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /DTDLValidator-Sample/DTDLValidator/Interactive/ListCommand.cs: -------------------------------------------------------------------------------- 1 | using CommandLine; 2 | using DTDLParser; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace DTDLValidator.Interactive 9 | { 10 | [Verb("list", HelpText = "List models.")] 11 | internal class ListCommand 12 | { 13 | public Task Run(Interactive p) 14 | { 15 | Console.WriteLine(listFormat, "Interface Id", "Display Name"); 16 | Console.WriteLine(listFormat, "------------", "------------"); 17 | foreach (DTInterfaceInfo @interface in p.Models.Values) 18 | { 19 | @interface.DisplayName.TryGetValue("en", out string displayName); 20 | Console.WriteLine(listFormat, @interface.Id.AbsoluteUri, displayName ?? ""); 21 | } 22 | 23 | return Task.FromResult(null); 24 | } 25 | 26 | private const string listFormat = "{0,-80}{1}"; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /DTDLValidator-Sample/DTDLValidator/Interactive/LoadCommand.cs: -------------------------------------------------------------------------------- 1 | using CommandLine; 2 | using DTDLParser; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics.CodeAnalysis; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | 10 | namespace DTDLValidator.Interactive 11 | { 12 | [Verb("load", HelpText = "Load models.")] 13 | internal class LoadCommand 14 | { 15 | [Value(0, HelpText = "List of file names to load.")] 16 | public IEnumerable FileNames { get; set; } 17 | 18 | public async Task Run(Interactive p) 19 | { 20 | List modelTexts = new List(); 21 | foreach (string fileName in FileNames) 22 | { 23 | string directoryName = Path.GetDirectoryName(fileName); 24 | if (string.IsNullOrWhiteSpace(directoryName)) 25 | { 26 | directoryName = "."; 27 | } 28 | 29 | string[] expandedFileNames = Directory.GetFiles(directoryName, Path.GetFileName(fileName)); 30 | foreach (string expandedFileName in expandedFileNames) 31 | { 32 | modelTexts.Add(File.ReadAllText(expandedFileName)); 33 | Console.WriteLine($"Loaded {expandedFileName}"); 34 | } 35 | } 36 | 37 | // Parse the models. 38 | // The set of entities returned from ParseAsync includes entities loaded by the resolver. 39 | Console.WriteLine(); 40 | try 41 | { 42 | (IReadOnlyDictionary entities, IEnumerable resolvedInterfaces) = await p.DTDLParser.ParseAsync(modelTexts.AsAsyncEnumerable()); 43 | foreach (Dtmi entityDtmi in entities.Keys) 44 | { 45 | Log.Ok($"Parsed {entityDtmi.AbsoluteUri}"); 46 | } 47 | 48 | // Store only the newly loaded interfaces. 49 | // Because the entities returned from ParseAsync contains 50 | // more than just interfaces and also any entities loaded by the resolver: 51 | // - Filter to just interfaces 52 | // - Exclude interfaces that were loaded by the resolver. 53 | // The above seems reasonable for a client to do, since the parser 54 | // doesn't/shouldn't know these details. 55 | Console.WriteLine(); 56 | IEnumerable interfaces = from entity in entities.Values 57 | where entity.EntityKind == DTEntityKind.Interface 58 | select entity as DTInterfaceInfo; 59 | interfaces = interfaces.Except(resolvedInterfaces, new DTInterfaceInfoComparer()); 60 | foreach (DTInterfaceInfo @interface in interfaces) 61 | { 62 | p.Models.Add(@interface.Id, @interface); 63 | Console.WriteLine($"Stored {@interface.Id.AbsoluteUri}"); 64 | } 65 | } 66 | catch (ParsingException pe) 67 | { 68 | Log.Error($"*** Error parsing models"); 69 | int derrcount = 1; 70 | foreach (ParsingError err in pe.Errors) 71 | { 72 | Log.Error($"Error {derrcount}:"); 73 | Log.Error($"{err.Message}"); 74 | Log.Error($"Primary ID: {err.PrimaryID}"); 75 | Log.Error($"Secondary ID: {err.SecondaryID}"); 76 | Log.Error($"Property: {err.Property}\n"); 77 | derrcount++; 78 | } 79 | } 80 | } 81 | 82 | private class DTInterfaceInfoComparer : IEqualityComparer 83 | { 84 | public bool Equals([AllowNull] DTInterfaceInfo x, [AllowNull] DTInterfaceInfo y) 85 | { 86 | if (ReferenceEquals(x, y)) 87 | { 88 | return true; 89 | } 90 | 91 | if (ReferenceEquals(x, null) || ReferenceEquals(y, null)) 92 | { 93 | return false; 94 | } 95 | 96 | return x.Id == y.Id; 97 | } 98 | 99 | public int GetHashCode([DisallowNull] DTInterfaceInfo obj) 100 | { 101 | return obj.Id.GetHashCode(); 102 | } 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /DTDLValidator-Sample/DTDLValidator/Interactive/ShowCommand.cs: -------------------------------------------------------------------------------- 1 | using CommandLine; 2 | using DTDLParser; 3 | using System; 4 | using System.Threading.Tasks; 5 | 6 | namespace DTDLValidator.Interactive 7 | { 8 | [Verb("show", HelpText = "Display model definition.")] 9 | internal class ShowCommand 10 | { 11 | [Value(0, HelpText = "Model id to show.")] 12 | public string ModelId { get; set; } 13 | 14 | public Task Run(Interactive p) 15 | { 16 | if (ModelId == null) 17 | { 18 | Log.Error("Please specify a valid model id"); 19 | return Task.FromResult(null); 20 | } 21 | try 22 | { 23 | Dtmi modelId = new Dtmi(ModelId); 24 | 25 | if (p.Models.TryGetValue(modelId, out DTInterfaceInfo @interface)) 26 | { 27 | Console.WriteLine(@interface.GetJsonLdText()); 28 | } 29 | } 30 | catch (Exception) 31 | { 32 | Log.Error($"{ModelId} is not a valid dtmi"); 33 | } 34 | 35 | return Task.FromResult(null); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /DTDLValidator-Sample/DTDLValidator/Interactive/ShowInfoCommand.cs: -------------------------------------------------------------------------------- 1 | using CommandLine; 2 | using DTDLParser; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | 8 | namespace DTDLValidator.Interactive 9 | { 10 | [Verb("showinfo", HelpText = "Display parent interfaces, properties and relationships defined in a model, taking inheritance into account")] 11 | internal class ShowInfoCommand 12 | { 13 | [Value(0, HelpText = "Model id to show.")] 14 | public string ModelId { get; set; } 15 | 16 | public Task Run(Interactive p) 17 | { 18 | if (ModelId == null) 19 | { 20 | Log.Error("Please specify a valid model id"); 21 | return Task.FromResult(null); 22 | } 23 | try 24 | { 25 | Dtmi modelId = new Dtmi(ModelId); 26 | 27 | if (p.Models.TryGetValue(modelId, out DTInterfaceInfo dti)) 28 | { 29 | Log.Ok("Inherited interfaces:"); 30 | foreach (DTInterfaceInfo parent in dti.Extends) 31 | { 32 | Log.Ok($" {parent.Id}"); 33 | } 34 | IReadOnlyDictionary contents = dti.Contents; 35 | Log.Alert($" Properties:"); 36 | var props = contents 37 | .Where(p => p.Value.EntityKind == DTEntityKind.Property) 38 | .Select(p => p.Value); 39 | foreach (DTPropertyInfo pi in props) 40 | { 41 | pi.Schema.DisplayName.TryGetValue("en", out string displayName); 42 | Log.Out($" {pi.Name}: {displayName ?? pi.Schema.ToString()}"); 43 | } 44 | Log.Out($" Relationships:", ConsoleColor.DarkMagenta); 45 | var rels = contents 46 | .Where(p => p.Value.EntityKind == DTEntityKind.Relationship) 47 | .Select(p => p.Value); 48 | foreach (DTRelationshipInfo ri in rels) 49 | { 50 | string target = ""; 51 | if (ri.Target != null) 52 | target = ri.Target.ToString(); 53 | Log.Out($" {ri.Name} -> {target}"); 54 | } 55 | } 56 | } 57 | catch (Exception) 58 | { 59 | Log.Error($"{ModelId} is not a valid dtmi"); 60 | } 61 | 62 | return Task.FromResult(null); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /DTDLValidator-Sample/DTDLValidator/ListExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace DTDLValidator 8 | { 9 | public static class ListExtensions 10 | { 11 | public static async IAsyncEnumerable AsAsyncEnumerable(this IEnumerable input) 12 | { 13 | foreach (var value in input) 14 | { 15 | yield return value; 16 | } 17 | await Task.Yield(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /DTDLValidator-Sample/DTDLValidator/Log.cs: -------------------------------------------------------------------------------- 1 | namespace DTDLValidator 2 | { 3 | using System; 4 | 5 | public static class Log 6 | { 7 | static public void Out(string s, ConsoleColor col = ConsoleColor.White) 8 | { 9 | Console.ForegroundColor = col; 10 | Console.WriteLine(s); 11 | Console.ForegroundColor = ConsoleColor.White; 12 | } 13 | 14 | static public void Error(string s) 15 | { 16 | Out(s, ConsoleColor.DarkRed); 17 | } 18 | 19 | static public void Alert(string s) 20 | { 21 | Out(s, ConsoleColor.DarkYellow); 22 | } 23 | 24 | static public void Ok(string s) 25 | { 26 | Out(s, ConsoleColor.DarkGreen); 27 | } 28 | 29 | static public void Error(Exception ex, string s) 30 | { 31 | var exception = s + "\n" + ex.ToString(); 32 | Out(exception, ConsoleColor.DarkRed); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /DTDLValidator-Sample/DTDLValidator/Program.cs: -------------------------------------------------------------------------------- 1 | using CommandLine; 2 | using DTDLParser; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Reflection; 8 | using System.Text.Json; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | 12 | namespace DTDLValidator 13 | { 14 | class Program 15 | { 16 | public class Options 17 | { 18 | [Option('e', "extension", Default = "json", SetName = "normal", HelpText = "File extension of files to be processed.")] 19 | public string Extension { get; set; } 20 | 21 | [Option('d', "directory", Default = ".", SetName = "normal", HelpText = "Directory to search files in.")] 22 | public string Directory { get; set; } 23 | 24 | [Option('r', "recursive", Default = true, SetName = "normal", HelpText = "Search given directory (option -d) only (false) or subdirectories too (true)")] 25 | public bool Recursive { get; set; } 26 | 27 | //[Option('f', "files", HelpText = "Input files to be processed. If -d option is also specified, these files are read in addition.")] 28 | //public IEnumerable InputFiles { get; set; } 29 | 30 | [Option('i', "interactive", Default = false, SetName = "interactive", HelpText = "Run in interactive mode")] 31 | public bool Interactive { get; set; } 32 | } 33 | 34 | static void Main(string[] args) 35 | { 36 | CommandLine.Parser.Default.ParseArguments(args) 37 | .WithParsed(RunOptions) 38 | .WithNotParsed(HandleParseError); 39 | } 40 | 41 | static void RunOptions(Options opts) 42 | { 43 | Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); 44 | string dtdlParserVersion = ""; 45 | foreach (Assembly a in assemblies) 46 | { 47 | if (a.GetName().Name.EndsWith("DigitalTwins.Parser")) 48 | dtdlParserVersion = a.GetName().Version.ToString(); 49 | } 50 | Log.Ok($"Simple DTDL Validator (dtdl parser library version {dtdlParserVersion})"); 51 | 52 | if (opts.Interactive == true) 53 | { 54 | Log.Alert("Entering interactive mode"); 55 | Interactive.Interactive i = new Interactive.Interactive(); 56 | return; 57 | } 58 | 59 | DirectoryInfo dinfo = null; 60 | try 61 | { 62 | dinfo = new DirectoryInfo(opts.Directory); 63 | } catch (Exception e) 64 | { 65 | Log.Error($"Error accessing the target directory '{opts.Directory}': \n{e.Message}"); 66 | return; 67 | } 68 | Log.Alert($"Validating *.{opts.Extension} files in folder '{dinfo.FullName}'.\nRecursive is set to {opts.Recursive}\n"); 69 | if (dinfo.Exists == false) 70 | { 71 | Log.Error($"Specified directory '{opts.Directory}' does not exist: Exiting..."); 72 | return; 73 | } 74 | else 75 | { 76 | SearchOption searchOpt = opts.Recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; 77 | var files = dinfo.EnumerateFiles($"*.{opts.Extension}", searchOpt); 78 | if (files.Count() == 0) 79 | { 80 | Log.Alert("No matching files found. Exiting."); 81 | return; 82 | } 83 | Dictionary modelDict = new Dictionary(); 84 | int count = 0; 85 | string lastFile = ""; 86 | try 87 | { 88 | foreach (FileInfo fi in files) 89 | { 90 | StreamReader r = new StreamReader(fi.FullName); 91 | string dtdl = r.ReadToEnd(); r.Close(); 92 | modelDict.Add(fi, dtdl); 93 | lastFile = fi.FullName; 94 | count++; 95 | } 96 | } catch (Exception e) 97 | { 98 | Log.Error($"Could not read files. \nLast file read: {lastFile}\nError: \n{e.Message}"); 99 | return; 100 | } 101 | Log.Ok($"Read {count} files from specified directory"); 102 | int errJson = 0; 103 | foreach (FileInfo fi in modelDict.Keys) 104 | { 105 | modelDict.TryGetValue(fi, out string dtdl); 106 | try 107 | { 108 | JsonDocument.Parse(dtdl); 109 | } catch (Exception e) 110 | { 111 | Log.Error($"Invalid json found in file {fi.FullName}.\nJson parser error \n{e.Message}"); 112 | errJson++; 113 | } 114 | } 115 | if (errJson > 0) 116 | { 117 | Log.Error($"\nFound {errJson} Json parsing errors"); 118 | return; 119 | } 120 | Log.Ok($"Validated JSON for all files - now validating DTDL"); 121 | List modelList = modelDict.Values.ToList(); 122 | ModelParser parser = new ModelParser( 123 | new ParsingOptions() 124 | { 125 | DtmiResolverAsync = Resolver 126 | } 127 | ); 128 | 129 | try 130 | { 131 | IReadOnlyDictionary om = parser.Parse(modelList); 132 | Log.Out(""); 133 | Log.Ok($"**********************************************"); 134 | Log.Ok($"** Validated all files - Your DTDL is valid **"); 135 | Log.Ok($"**********************************************"); 136 | Log.Out($"Found a total of {om.Keys.Count()} entities"); 137 | } 138 | catch (ParsingException pe) 139 | { 140 | Log.Error($"*** Error parsing models"); 141 | int derrcount = 1; 142 | foreach (ParsingError err in pe.Errors) 143 | { 144 | Log.Error($"Error {derrcount}:"); 145 | Log.Error($"{err.Message}"); 146 | Log.Error($"Primary ID: {err.PrimaryID}"); 147 | Log.Error($"Secondary ID: {err.SecondaryID}"); 148 | Log.Error($"Property: {err.Property}\n"); 149 | derrcount++; 150 | } 151 | return; 152 | } 153 | catch (ResolutionException rex) 154 | { 155 | Log.Error("Could not resolve required references: " + rex.Message); 156 | } 157 | } 158 | } 159 | 160 | #pragma warning disable CS8425 // Async-iterator member has one or more parameters of type 'CancellationToken' but none of them is decorated with the 'EnumeratorCancellation' attribute, so the cancellation token parameter from the generated 'IAsyncEnumerable<>.GetAsyncEnumerator' will be unconsumed 161 | static async IAsyncEnumerable Resolver(IReadOnlyCollection dtmis, CancellationToken cancellationToken) 162 | #pragma warning restore CS8425 // Async-iterator member has one or more parameters of type 'CancellationToken' but none of them is decorated with the 'EnumeratorCancellation' attribute, so the cancellation token parameter from the generated 'IAsyncEnumerable<>.GetAsyncEnumerator' will be unconsumed 163 | { 164 | Log.Error($"*** Error parsing models. Missing:"); 165 | foreach (Dtmi d in dtmis) 166 | { 167 | Log.Error($" {d}"); 168 | } 169 | await Task.Yield(); 170 | yield return null; 171 | } 172 | 173 | static void HandleParseError(IEnumerable errs) 174 | { 175 | Log.Error($"Invalid command line."); 176 | foreach (Error e in errs) 177 | { 178 | Log.Error($"{e.Tag}: {e.ToString()}"); 179 | } 180 | 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | languages: 4 | - csharp 5 | products: 6 | - azure-digital-twins 7 | - azure-iot-pnp 8 | name: DTDL Validator 9 | description: A code sample for validating DTDL model code 10 | urlFragment: dtdl-validator 11 | --- 12 | 13 | *Status note on 6/9/23: This project is being archived in favor of the examples in the [DTDL parser repository](https://github.com/digitaltwinconsortium/DTDLParser/tree/main/samples/DTDLParserResolveSample), which are more recently maintained.* 14 | 15 | # Introduction 16 | This project demonstrates use of the Azure Digital Twins DTDL parser, available [here](https://nuget.org/packages/Microsoft.Azure.DigitalTwins.Parser/) on NuGet. It is language-agnostic, and can be used as a command line utility to validate a directory tree of DTDL files. It also provides an interactive mode. 17 | 18 | The source code shows examples for how to use the parser library, and can validate model documents to make sure the DTDL is valid. 19 | 20 | # Getting started 21 | The program is a command line application that can be used in normal or interactive mode. 22 | 23 | In normal mode, specify: 24 | * a file extension (-e, default json) 25 | * a directory to search (-d, no default value) 26 | * a recursive option that determines if the file search descends into subdirectories (-r, default false) 27 | 28 | Interactive mode is entered with the -i option. Type help for information on interactive commands 29 | 30 | # What the code demonstrates 31 | * Basic use of the DTDL parser for validation of DTDL 32 | * Basic use of the object model to access information about DTDL content (see the interactive module, in particular the list and show/showinfo commands) 33 | 34 | # Build and test 35 | Build the project and run the application from the command line. 36 | 37 | You can also create a self-contained single-file .exe (no other files or installations required): 38 | 39 | Run 40 | ```bash 41 | dotnet publish -r win-x64 -c Release /p:PublishSingleFile=true 42 | ``` 43 | in the root folder of the repo. 44 | 45 | # Package as Global Tool 46 | Run 47 | ``` bash 48 | dotnet publish 49 | dotnet tool install --global --add-source ./DTDLValidator/nupkg DTDLValidator 50 | ``` 51 | 52 | This appends the path of the generated executible to your system's **PATH** variable. 53 | Now, run `dtdl-validator ` to use the tool. 54 | 55 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 40 | 41 | --------------------------------------------------------------------------------