├── .gitignore ├── CosmosDBStudio.sln ├── CosmosDBStudio.sln.DotSettings ├── Directory.Build.props ├── LICENSE ├── README.md ├── assets ├── attributions.txt ├── compass.png ├── cosmosdb-small.png ├── cosmosdb.pdn ├── cosmosdb.png ├── planet.png └── screenshots │ └── CosmosDBStudio-screenshot.png ├── global.json ├── pkg └── CosmosDBStudio.Packaging │ ├── CosmosDBStudio.Packaging.wapproj │ ├── Images │ ├── BadgeLogo.scale-100.png │ ├── BadgeLogo.scale-125.png │ ├── BadgeLogo.scale-150.png │ ├── BadgeLogo.scale-200.png │ ├── BadgeLogo.scale-400.png │ ├── LargeTile.scale-100.png │ ├── LargeTile.scale-125.png │ ├── LargeTile.scale-150.png │ ├── LargeTile.scale-200.png │ ├── LargeTile.scale-400.png │ ├── LockScreenLogo.scale-200.png │ ├── SmallTile.scale-100.png │ ├── SmallTile.scale-125.png │ ├── SmallTile.scale-150.png │ ├── SmallTile.scale-200.png │ ├── SmallTile.scale-400.png │ ├── SplashScreen.scale-100.png │ ├── SplashScreen.scale-125.png │ ├── SplashScreen.scale-150.png │ ├── SplashScreen.scale-200.png │ ├── SplashScreen.scale-400.png │ ├── Square150x150Logo.scale-100.png │ ├── Square150x150Logo.scale-125.png │ ├── Square150x150Logo.scale-150.png │ ├── Square150x150Logo.scale-200.png │ ├── Square150x150Logo.scale-400.png │ ├── Square44x44Logo.altform-lightunplated_targetsize-16.png │ ├── Square44x44Logo.altform-lightunplated_targetsize-24.png │ ├── Square44x44Logo.altform-lightunplated_targetsize-256.png │ ├── Square44x44Logo.altform-lightunplated_targetsize-32.png │ ├── Square44x44Logo.altform-lightunplated_targetsize-48.png │ ├── Square44x44Logo.altform-unplated_targetsize-16.png │ ├── Square44x44Logo.altform-unplated_targetsize-256.png │ ├── Square44x44Logo.altform-unplated_targetsize-32.png │ ├── Square44x44Logo.altform-unplated_targetsize-48.png │ ├── Square44x44Logo.scale-100.png │ ├── Square44x44Logo.scale-125.png │ ├── Square44x44Logo.scale-150.png │ ├── Square44x44Logo.scale-200.png │ ├── Square44x44Logo.scale-400.png │ ├── Square44x44Logo.targetsize-16.png │ ├── Square44x44Logo.targetsize-24.png │ ├── Square44x44Logo.targetsize-24_altform-unplated.png │ ├── Square44x44Logo.targetsize-256.png │ ├── Square44x44Logo.targetsize-32.png │ ├── Square44x44Logo.targetsize-48.png │ ├── StoreLogo.backup.png │ ├── StoreLogo.scale-100.png │ ├── StoreLogo.scale-125.png │ ├── StoreLogo.scale-150.png │ ├── StoreLogo.scale-200.png │ ├── StoreLogo.scale-400.png │ ├── Wide310x150Logo.scale-100.png │ ├── Wide310x150Logo.scale-125.png │ ├── Wide310x150Logo.scale-150.png │ ├── Wide310x150Logo.scale-200.png │ └── Wide310x150Logo.scale-400.png │ ├── Package.StoreAssociation.xml │ └── Package.appxmanifest └── src ├── CosmosDBStudio.Model ├── CosmosAccount.cs ├── CosmosAccountFolder.cs ├── CosmosContainer.cs ├── CosmosDBStudio.Model.csproj ├── CosmosDatabase.cs ├── CosmosStoredProcedure.cs ├── CosmosTrigger.cs ├── CosmosUserDefinedFunction.cs ├── Helpers │ └── CosmosHelper.cs ├── ICosmosItem.cs ├── ICosmosScript.cs ├── OperationResult.cs ├── Query.cs ├── QueryResult.cs ├── QuerySheet.cs ├── ScalarValueType.cs ├── Services │ ├── IAccountContext.cs │ ├── IAccountContextFactory.cs │ ├── IAccountDirectory.cs │ ├── IApplication.cs │ ├── IClientPool.cs │ ├── IContainerContext.cs │ ├── IContainerService.cs │ ├── IDatabaseContext.cs │ ├── IDatabaseService.cs │ ├── IDocumentService.cs │ ├── IMessenger.cs │ ├── IQueryPersistenceService.cs │ ├── IQueryService.cs │ ├── IRequestOptionsBuilder.cs │ ├── IScriptService.cs │ └── Implementation │ │ ├── AccountContext.cs │ │ ├── AccountContextFactory.cs │ │ ├── AccountDirectory.cs │ │ ├── ClientPool.cs │ │ ├── ContainerContext.cs │ │ ├── ContainerService.cs │ │ ├── DatabaseContext.cs │ │ ├── DatabaseService.cs │ │ ├── DocumentService.cs │ │ ├── ItemRequestOptionsBuilder.cs │ │ ├── PartitionKeyHelper.cs │ │ ├── QueryPersistenceService.cs │ │ ├── QueryRequestOptionsBuilder.cs │ │ ├── QueryService.cs │ │ ├── RequestOptionsBuilderBase.cs │ │ └── ScriptService.cs ├── StoredProcedureResult.cs └── Workspace.cs ├── CosmosDBStudio.Util ├── CosmosDBStudio.Util.csproj ├── Extensions │ ├── JTokenExtensions.cs │ ├── LinkedListExtensions.cs │ ├── ObservableCollectionExtensions.cs │ └── ServiceCollectionExtensions.cs └── Helpers │ └── JsonHelper.cs ├── CosmosDBStudio.ViewModel ├── AboutViewModel.cs ├── AccountsViewModel.cs ├── CommandViewModel.cs ├── Commands │ ├── AccountCommands.cs │ ├── CommandExtensions.cs │ ├── ContainerCommands.cs │ ├── DatabaseCommands.cs │ └── ScriptCommands.cs ├── CosmosDBStudio.ViewModel.csproj ├── Dialogs │ ├── AccountEditorViewModel.cs │ ├── ContainerEditorViewModel.cs │ ├── ContainerPickerViewModel.cs │ ├── DatabaseEditorViewModel.cs │ ├── DialogButton.cs │ ├── DialogClosingEventArgs.cs │ ├── DialogViewModelBase.cs │ ├── DocumentEditorViewModel.cs │ ├── IDialogViewModel.cs │ └── TextPromptViewModel.cs ├── EditorTabs │ ├── ParameterViewModelBase.cs │ ├── ParametersViewModel.cs │ ├── Queries │ │ ├── DocumentResultViewModel.cs │ │ ├── EmptyResultItemPlaceholderViewModel.cs │ │ ├── ErrorItemPlaceholderViewModel.cs │ │ ├── NotRunQueryResultViewModel.cs │ │ ├── QueryParameterViewModel.cs │ │ ├── QueryResultViewModel.cs │ │ ├── QueryResultViewModelBase.cs │ │ └── ResultItemViewModel.cs │ ├── QuerySheetViewModel.cs │ ├── ScriptEditorViewModelBase.cs │ ├── StoredProcedureEditorViewModel.cs │ ├── StoredProcedureResultViewModel.cs │ ├── TabViewModelBase.cs │ ├── TriggerEditorViewModel.cs │ └── UserDefinedFunctionEditorViewModel.cs ├── ISaveable.cs ├── IViewModelFactory.cs ├── MainWindowViewModel.cs ├── Messages │ ├── AccountAddedMessage.cs │ ├── AccountEditedMessage.cs │ ├── AccountRemovedMessage.cs │ ├── ContainerCreatedMessage.cs │ ├── ContainerDeletedMessage.cs │ ├── DatabaseCreatedMessage.cs │ ├── DatabaseDeletedMessage.cs │ ├── ExplorerSelectedContainerChangedMessage.cs │ ├── NewQuerySheetMessage.cs │ ├── OpenScriptMessage.cs │ └── SetStatusBarMessage.cs ├── Services │ ├── IClipboardService.cs │ ├── IDialogService.cs │ ├── IUIDispatcher.cs │ └── Implementation │ │ └── Messenger.cs ├── TreeNodes │ ├── AccountFolderNodeViewModel.cs │ ├── AccountNodeViewModel.cs │ ├── ContainerNodeViewModel.cs │ ├── DatabaseNodeViewModel.cs │ ├── NonLeafTreeNodeViewModel.cs │ ├── ScriptFolderNodeViewModel.cs │ ├── ScriptNodeViewModel.cs │ ├── StoredProcedureNodeViewModel.cs │ ├── StoredProceduresFolderNodeViewModel.cs │ ├── TreeNodeViewModel.cs │ ├── TriggerNodeViewModel.cs │ ├── TriggersFolderNodeViewModel.cs │ ├── UserDefinedFunctionNodeViewModel.cs │ └── UserDefinedFunctionsFolderNodeViewModel.cs ├── ViewModelFactoryProxy.cs └── ViewModelValidator.cs └── CosmosDBStudio ├── App.xaml ├── App.xaml.cs ├── Behaviors ├── AvalonTextEditorBehavior.cs ├── AvalonTextEditorBindingBehavior.cs ├── ErrorBehavior.cs ├── FocusOnLoadBehavior.cs ├── OpenContextMenuBehavior.cs └── TreeViewSelectedItemBindingBehavior.cs ├── Converters ├── EnumToInt32Converter.cs ├── NotConverter.cs └── SuppressAccessKeyConverter.cs ├── CosmosDBStudio.csproj ├── DataTemplates.xaml ├── Extensions ├── CommandExtensions.cs └── UIElementExtensions.cs ├── Images ├── account.png ├── app.ico ├── container.png ├── database.png ├── folder.png ├── hi-res │ ├── account.png │ ├── container.png │ ├── database.png │ ├── stored-procedure.png │ ├── trigger.png │ └── user-defined-function.png ├── logo.png ├── stored-procedure.png ├── trigger.png └── user-defined-function.png ├── MainWindow.xaml ├── MainWindow.xaml.cs ├── Markup ├── BindingProxy.cs ├── EnumValuesExtension.cs └── SwitchExtension.cs ├── Program.cs ├── Properties ├── AssemblyInfo.cs ├── Resources.Designer.cs ├── Resources.resx ├── Settings.Designer.cs └── Settings.settings ├── Resources.xaml ├── Resources └── fa-solid-900.ttf ├── Services └── Implementation │ ├── ClipboardService.cs │ ├── DialogService.cs │ └── UIDispatcher.cs ├── Styles.xaml ├── SyntaxHighlighting ├── CosmosJS.xshd ├── CosmosSQL.xshd ├── CosmosSyntax.cs └── JSON.xshd ├── Themes ├── Generic.xaml ├── RadioChoice.generic.xaml └── TreeNodeControl.generic.xaml └── View ├── AboutView.xaml ├── AboutView.xaml.cs ├── AccountEditorView.xaml ├── AccountEditorView.xaml.cs ├── AccountsView.xaml ├── AccountsView.xaml.cs ├── ContainerEditorView.xaml ├── ContainerEditorView.xaml.cs ├── ContainerPickerView.xaml ├── ContainerPickerView.xaml.cs ├── Controls ├── AccountExplorerTreeView.cs ├── AccountExplorerTreeViewItem.cs ├── CosmosTabControl.cs ├── RadioChoice.cs └── TreeNodeControl.cs ├── CosmosTabItem.xaml ├── CosmosTabItem.xaml.cs ├── DatabaseEditorView.xaml ├── DatabaseEditorView.xaml.cs ├── DialogWindow.xaml ├── DialogWindow.xaml.cs ├── DocumentEditorView.xaml ├── DocumentEditorView.xaml.cs ├── QueryParametersView.xaml ├── QueryParametersView.xaml.cs ├── QueryResultsView.xaml ├── QueryResultsView.xaml.cs ├── QuerySheetView.xaml ├── QuerySheetView.xaml.cs ├── StoredProcedureEditorView.xaml ├── StoredProcedureEditorView.xaml.cs ├── StoredProcedureParametersView.xaml ├── StoredProcedureParametersView.xaml.cs ├── StoredProcedureResultView.xaml ├── StoredProcedureResultView.xaml.cs ├── TextPromptView.xaml ├── TextPromptView.xaml.cs ├── TriggerEditorView.xaml ├── TriggerEditorView.xaml.cs ├── UserDefinedFunctionEditorView.xaml └── UserDefinedFunctionEditorView.xaml.cs /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | obj/ 3 | .vs/ 4 | .idea/ 5 | _ReSharper.Caches/ 6 | *.user 7 | 8 | AppPackages/ 9 | BundleArtifacts/ 10 | -------------------------------------------------------------------------------- /CosmosDBStudio.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10.0 4 | enable 5 | 6 | 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Thomas Levesque 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 | -------------------------------------------------------------------------------- /assets/attributions.txt: -------------------------------------------------------------------------------- 1 | Compass by Adrien Coquet from the Noun Project (https://thenounproject.com/browse/?i=1941277) 2 | Planet by Markus from the Noun Project (https://thenounproject.com/browse/?i=2145589) -------------------------------------------------------------------------------- /assets/compass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/assets/compass.png -------------------------------------------------------------------------------- /assets/cosmosdb-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/assets/cosmosdb-small.png -------------------------------------------------------------------------------- /assets/cosmosdb.pdn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/assets/cosmosdb.pdn -------------------------------------------------------------------------------- /assets/cosmosdb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/assets/cosmosdb.png -------------------------------------------------------------------------------- /assets/planet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/assets/planet.png -------------------------------------------------------------------------------- /assets/screenshots/CosmosDBStudio-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/assets/screenshots/CosmosDBStudio-screenshot.png -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "6.0.100" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/BadgeLogo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/BadgeLogo.scale-100.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/BadgeLogo.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/BadgeLogo.scale-125.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/BadgeLogo.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/BadgeLogo.scale-150.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/BadgeLogo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/BadgeLogo.scale-200.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/BadgeLogo.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/BadgeLogo.scale-400.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/LargeTile.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/LargeTile.scale-100.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/LargeTile.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/LargeTile.scale-125.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/LargeTile.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/LargeTile.scale-150.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/LargeTile.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/LargeTile.scale-200.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/LargeTile.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/LargeTile.scale-400.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/LockScreenLogo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/LockScreenLogo.scale-200.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/SmallTile.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/SmallTile.scale-100.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/SmallTile.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/SmallTile.scale-125.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/SmallTile.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/SmallTile.scale-150.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/SmallTile.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/SmallTile.scale-200.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/SmallTile.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/SmallTile.scale-400.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/SplashScreen.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/SplashScreen.scale-100.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/SplashScreen.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/SplashScreen.scale-125.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/SplashScreen.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/SplashScreen.scale-150.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/SplashScreen.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/SplashScreen.scale-200.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/SplashScreen.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/SplashScreen.scale-400.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Square150x150Logo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Square150x150Logo.scale-100.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Square150x150Logo.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Square150x150Logo.scale-125.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Square150x150Logo.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Square150x150Logo.scale-150.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Square150x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Square150x150Logo.scale-200.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Square150x150Logo.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Square150x150Logo.scale-400.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.altform-lightunplated_targetsize-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.altform-lightunplated_targetsize-16.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.altform-lightunplated_targetsize-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.altform-lightunplated_targetsize-24.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.altform-lightunplated_targetsize-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.altform-lightunplated_targetsize-256.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.altform-lightunplated_targetsize-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.altform-lightunplated_targetsize-32.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.altform-lightunplated_targetsize-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.altform-lightunplated_targetsize-48.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.altform-unplated_targetsize-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.altform-unplated_targetsize-16.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.altform-unplated_targetsize-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.altform-unplated_targetsize-256.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.altform-unplated_targetsize-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.altform-unplated_targetsize-32.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.altform-unplated_targetsize-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.altform-unplated_targetsize-48.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.scale-100.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.scale-125.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.scale-150.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.scale-200.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.scale-400.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.targetsize-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.targetsize-16.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.targetsize-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.targetsize-24.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.targetsize-24_altform-unplated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.targetsize-24_altform-unplated.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.targetsize-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.targetsize-256.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.targetsize-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.targetsize-32.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.targetsize-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Square44x44Logo.targetsize-48.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/StoreLogo.backup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/StoreLogo.backup.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/StoreLogo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/StoreLogo.scale-100.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/StoreLogo.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/StoreLogo.scale-125.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/StoreLogo.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/StoreLogo.scale-150.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/StoreLogo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/StoreLogo.scale-200.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/StoreLogo.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/StoreLogo.scale-400.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Wide310x150Logo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Wide310x150Logo.scale-100.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Wide310x150Logo.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Wide310x150Logo.scale-125.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Wide310x150Logo.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Wide310x150Logo.scale-150.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Wide310x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Wide310x150Logo.scale-200.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Images/Wide310x150Logo.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/pkg/CosmosDBStudio.Packaging/Images/Wide310x150Logo.scale-400.png -------------------------------------------------------------------------------- /pkg/CosmosDBStudio.Packaging/Package.appxmanifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 8 | 9 | 13 | 14 | 15 | Cosmos DB Studio 16 | Thomas Levesque 17 | Images\StoreLogo.png 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/CosmosAccount.cs: -------------------------------------------------------------------------------- 1 | namespace CosmosDBStudio.Model 2 | { 3 | public class CosmosAccount 4 | { 5 | public string Id { get; set; } = string.Empty; 6 | public string Name { get; set; } = string.Empty; 7 | public string Endpoint { get; set; } = string.Empty; 8 | public string Key { get; set; } = string.Empty; 9 | public bool IsServerless { get; set; } 10 | public string Folder { get; set; } = string.Empty; 11 | 12 | public CosmosAccount Clone() 13 | { 14 | return new CosmosAccount 15 | { 16 | Id = Id, 17 | Name = Name, 18 | Endpoint = Endpoint, 19 | Key = Key, 20 | IsServerless = IsServerless, 21 | Folder = Folder 22 | }; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/CosmosAccountFolder.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace CosmosDBStudio.Model 4 | { 5 | public class CosmosAccountFolder 6 | { 7 | public CosmosAccountFolder(string fullPath) 8 | { 9 | FullPath = fullPath; 10 | } 11 | 12 | public string Name => Path.GetFileName(FullPath); 13 | public string FullPath { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/CosmosContainer.cs: -------------------------------------------------------------------------------- 1 | namespace CosmosDBStudio.Model 2 | { 3 | public class CosmosContainer : ICosmosItem 4 | { 5 | public string Id { get; set; } = string.Empty; 6 | public string? ETag { get; set; } 7 | public string PartitionKeyPath { get; set; } = string.Empty; 8 | public bool LargePartitionKey { get; set; } 9 | public int? DefaultTTL { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/CosmosDBStudio.Model.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/CosmosDatabase.cs: -------------------------------------------------------------------------------- 1 | namespace CosmosDBStudio.Model 2 | { 3 | public class CosmosDatabase : ICosmosItem 4 | { 5 | public string Id { get; set; } = string.Empty; 6 | public string? ETag { get; set; } 7 | } 8 | } -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/CosmosStoredProcedure.cs: -------------------------------------------------------------------------------- 1 | namespace CosmosDBStudio.Model 2 | { 3 | public class CosmosStoredProcedure : ICosmosScript 4 | { 5 | public string Id { get; set; } = string.Empty; 6 | public string Body { get; set; } = string.Empty; 7 | public string? ETag { get; set; } = string.Empty; 8 | 9 | public ICosmosScript Clone() => (ICosmosScript)MemberwiseClone(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/CosmosTrigger.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.Cosmos.Scripts; 2 | 3 | namespace CosmosDBStudio.Model 4 | { 5 | public class CosmosTrigger : ICosmosScript 6 | { 7 | public string Id { get; set; } = string.Empty; 8 | public string Body { get; set; } = string.Empty; 9 | public string? ETag { get; set; } 10 | public TriggerOperation Operation { get; set; } 11 | public TriggerType Type { get; set; } 12 | 13 | public ICosmosScript Clone() => (ICosmosScript)MemberwiseClone(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/CosmosUserDefinedFunction.cs: -------------------------------------------------------------------------------- 1 | namespace CosmosDBStudio.Model 2 | { 3 | public class CosmosUserDefinedFunction : ICosmosScript 4 | { 5 | public string Id { get; set; } = string.Empty; 6 | public string Body { get; set; } = string.Empty; 7 | public string? ETag { get; set; } = string.Empty; 8 | 9 | public ICosmosScript Clone() => (ICosmosScript)MemberwiseClone(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/Helpers/CosmosHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | 3 | namespace CosmosDBStudio.Model.Helpers 4 | { 5 | public static class CosmosHelper 6 | { 7 | private static readonly char[] ForbiddenCharactersInId = @"/\#?".ToCharArray(); 8 | 9 | public static string ValidateId(string id, string? idNameInMessage = null) 10 | { 11 | idNameInMessage ??= "id"; 12 | 13 | if (string.IsNullOrEmpty(id)) 14 | return $"The {idNameInMessage} must be specified"; 15 | 16 | if (id.IndexOfAny(ForbiddenCharactersInId) >= 0) 17 | { 18 | return $"The {idNameInMessage} must not contain characters " 19 | + string.Join(" ", ForbiddenCharactersInId.Select(c => $"'{c}'")); 20 | } 21 | 22 | if (id.Trim() != id) 23 | { 24 | return $"The {idNameInMessage} must not start or end with space"; 25 | } 26 | 27 | return string.Empty; 28 | } 29 | 30 | public static string? ValidateThroughput(int? throughput) => ValidateThroughput(throughput ?? 0, throughput.HasValue); 31 | 32 | public static string ValidateThroughput(int throughput, bool isThroughputProvisioned) 33 | { 34 | if (isThroughputProvisioned) 35 | { 36 | if (throughput < 400) 37 | return "Throughput cannot be less than 400"; 38 | } 39 | 40 | return string.Empty; 41 | } 42 | 43 | public static string ValidateDefaultTTL(int? defaultTTL) => ValidateDefaultTTL(defaultTTL ?? 0, defaultTTL.HasValue); 44 | 45 | public static string ValidateDefaultTTL(int defaultTTL, bool hasDefaultTTL) 46 | { 47 | if (hasDefaultTTL) 48 | { 49 | if (defaultTTL < 1) 50 | return "Default TTL must be at least 1 second"; 51 | } 52 | 53 | return string.Empty; 54 | } 55 | 56 | public static string ValidatePartitionKeyPath(string? partitionKeyPath) 57 | { 58 | if (string.IsNullOrEmpty(partitionKeyPath)) 59 | return "The partition key must be specified"; 60 | 61 | if (partitionKeyPath.Trim() != partitionKeyPath) 62 | return "The partition key must not start or end with space"; 63 | 64 | if (!partitionKeyPath.StartsWith('/')) 65 | return "The partition key must start with '/'"; 66 | 67 | return string.Empty; 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/ICosmosItem.cs: -------------------------------------------------------------------------------- 1 | namespace CosmosDBStudio.Model 2 | { 3 | public interface ICosmosItem 4 | { 5 | string Id { get; set; } 6 | string? ETag { get; set; } 7 | } 8 | } -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/ICosmosScript.cs: -------------------------------------------------------------------------------- 1 | namespace CosmosDBStudio.Model 2 | { 3 | public interface ICosmosScript : ICosmosItem 4 | { 5 | string Body { get; set; } 6 | 7 | ICosmosScript Clone(); 8 | } 9 | } -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/OperationResult.cs: -------------------------------------------------------------------------------- 1 | namespace CosmosDBStudio.Model 2 | { 3 | public enum OperationResult 4 | { 5 | Success, 6 | AlreadyExists, 7 | EditConflict, 8 | NotFound, 9 | Forbidden 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/Query.cs: -------------------------------------------------------------------------------- 1 | using Hamlet; 2 | using System.Collections.Generic; 3 | 4 | namespace CosmosDBStudio.Model 5 | { 6 | public class Query 7 | { 8 | public Query(string sql) 9 | { 10 | Sql = sql; 11 | Parameters = new Dictionary(); 12 | } 13 | 14 | public string Sql { get; set; } 15 | public Option PartitionKey { get; set; } 16 | public IDictionary Parameters { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/QueryResult.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace CosmosDBStudio.Model 6 | { 7 | public class QueryResult 8 | { 9 | public QueryResult(Query query) 10 | { 11 | Query = query; 12 | } 13 | 14 | public Query Query { get; } 15 | public IReadOnlyList Items { get; set; } = Array.Empty(); 16 | public Exception? Error { get; set; } 17 | public TimeSpan TimeElapsed { get; set; } 18 | public double RequestCharge { get; set; } 19 | public string? ContinuationToken { get; set; } 20 | public IEnumerable Warnings { get; set; } = Array.Empty(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/QuerySheet.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CosmosDBStudio.Model 4 | { 5 | public class QuerySheet 6 | { 7 | private const string DefaultQuery = "select * from c"; 8 | public const string FileFilter = "Cosmos DB Studio query sheet|*.cdbsqs"; 9 | 10 | public string Text { get; set; } = DefaultQuery; 11 | public string? PartitionKey { get; set; } 12 | public IList PartitionKeyMRU { get; set; } = new List(); 13 | public IList Parameters { get; set; } = new List(); 14 | } 15 | 16 | public class QuerySheetParameter 17 | { 18 | public string Name { get; set; } = string.Empty; 19 | public string? RawValue { get; set; } 20 | public IList MRU { get; set; } = new List(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/ScalarValueType.cs: -------------------------------------------------------------------------------- 1 | namespace CosmosDBStudio.Model 2 | { 3 | public enum ScalarValueType 4 | { 5 | String, 6 | Number, 7 | Boolean 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/Services/IAccountContext.cs: -------------------------------------------------------------------------------- 1 | namespace CosmosDBStudio.Model.Services 2 | { 3 | public interface IAccountContext 4 | { 5 | string AccountId { get; } 6 | string AccountName { get; } 7 | bool IsServerless { get; } 8 | 9 | IDatabaseService Databases { get; } 10 | IDatabaseContext GetDatabaseContext(CosmosDatabase database); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/Services/IAccountContextFactory.cs: -------------------------------------------------------------------------------- 1 | namespace CosmosDBStudio.Model.Services 2 | { 3 | public interface IAccountContextFactory 4 | { 5 | IAccountContext Create(CosmosAccount account); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/Services/IAccountDirectory.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CosmosDBStudio.Model.Services 4 | { 5 | public interface IAccountDirectory 6 | { 7 | IEnumerable Accounts { get; } 8 | void Add(CosmosAccount account); 9 | void Remove(string id); 10 | void Update(CosmosAccount account); 11 | void Load(); 12 | void Save(); 13 | bool TryGetById(string id, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out CosmosAccount? account); 14 | IEnumerable GetRootNodes(); 15 | IEnumerable GetChildNodes(string folderPrefix); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/Services/IApplication.cs: -------------------------------------------------------------------------------- 1 | namespace CosmosDBStudio.Model.Services; 2 | 3 | public interface IApplication 4 | { 5 | void Quit(); 6 | 7 | ApplicationVersionInfo GetVersionInfo(); 8 | } 9 | 10 | public record ApplicationVersionInfo(string ProductName, string Version, string Author); -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/Services/IClientPool.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.Cosmos; 2 | 3 | namespace CosmosDBStudio.Model.Services 4 | { 5 | public interface IClientPool 6 | { 7 | CosmosClient GetClientForAccount(CosmosAccount account); 8 | void RemoveClientForAccount(CosmosAccount account); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/Services/IContainerContext.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace CosmosDBStudio.Model.Services 5 | { 6 | public interface IContainerContext 7 | { 8 | IDatabaseContext DatabaseContext { get; } 9 | string AccountId { get; } 10 | string AccountName { get; } 11 | string DatabaseId { get; } 12 | string ContainerId { get; } 13 | string Path => $"{AccountName}/{DatabaseId}/{ContainerId}"; 14 | string? PartitionKeyPath { get; } 15 | string? PartitionKeyJsonPath { get; } 16 | IDocumentService Documents { get; } 17 | IQueryService Query { get; } 18 | IScriptService Scripts { get; } 19 | 20 | Task GetContainerAsync(CancellationToken cancellationToken); 21 | 22 | Task GetThroughputAsync(CancellationToken cancellationToken); 23 | Task SetThroughputAsync(int? throughput, CancellationToken cancellationToken); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/Services/IContainerService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace CosmosDBStudio.Model.Services 5 | { 6 | public interface IContainerService 7 | { 8 | Task GetContainersAsync(CancellationToken cancellationToken); 9 | Task GetContainerAsync(string containerId, CancellationToken cancellationToken); 10 | Task CreateContainerAsync(CosmosContainer container, int? throughput, CancellationToken cancellationToken); 11 | Task UpdateContainerAsync(CosmosContainer container, CancellationToken cancellationToken); 12 | Task DeleteContainerAsync(CosmosContainer container, CancellationToken cancellationToken); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/Services/IDatabaseContext.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace CosmosDBStudio.Model.Services 5 | { 6 | public interface IDatabaseContext 7 | { 8 | IAccountContext AccountContext { get; } 9 | string AccountId { get; } 10 | string AccountName { get; } 11 | string DatabaseId { get; } 12 | IContainerService Containers { get; } 13 | 14 | IContainerContext GetContainerContext(CosmosContainer container, CancellationToken cancellationToken); 15 | 16 | Task GetDatabaseAsync(CancellationToken cancellationToken); 17 | 18 | Task GetThroughputAsync(CancellationToken cancellationToken); 19 | Task SetThroughputAsync(int? throughput, CancellationToken cancellationToken); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/Services/IDatabaseService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace CosmosDBStudio.Model.Services 5 | { 6 | public interface IDatabaseService 7 | { 8 | Task GetDatabasesAsync(CancellationToken cancellationToken); 9 | Task GetDatabaseAsync(string databaseId, CancellationToken cancellationToken); 10 | Task CreateDatabaseAsync(CosmosDatabase database, int? throughput, CancellationToken cancellationToken); 11 | Task DeleteDatabaseAsync(CosmosDatabase database, CancellationToken cancellationToken); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/Services/IDocumentService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using Hamlet; 4 | using Newtonsoft.Json.Linq; 5 | 6 | namespace CosmosDBStudio.Model.Services 7 | { 8 | public interface IDocumentService 9 | { 10 | Task CreateAsync(JObject document, Option partitionKey, CancellationToken cancellationToken); 11 | Task GetAsync(string id, Option partitionKey, CancellationToken cancellationToken); 12 | Task ReplaceAsync(string id, JObject document, Option partitionKey, string? eTag, CancellationToken cancellationToken); 13 | Task DeleteAsync(string id, Option partitionKey, string? eTag, CancellationToken cancellationToken); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/Services/IMessenger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CosmosDBStudio.Model.Services 4 | { 5 | public interface IMessenger 6 | { 7 | void Publish(TMessage message); 8 | 9 | IMessengerSubscriptionBuilder Subscribe(TSubscriber subscriber) 10 | where TSubscriber : class; 11 | } 12 | 13 | public interface IMessengerSubscriptionBuilder 14 | where TSubscriber : class 15 | { 16 | IDisposable To(Action handler, bool handleDerivedTypes = false); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/Services/IQueryPersistenceService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CosmosDBStudio.Model.Services 4 | { 5 | public interface IQueryPersistenceService 6 | { 7 | QuerySheet? Load(string path); 8 | void Save(QuerySheet querySheet, string path); 9 | IList LoadMruList(); 10 | void SaveMruList(IList mruList); 11 | 12 | string SaveWorkspaceTempQuery(QuerySheet querySheet); 13 | void SaveWorkspace(Workspace workspace); 14 | Workspace LoadWorkspace(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/Services/IQueryService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace CosmosDBStudio.Model.Services 5 | { 6 | public interface IQueryService 7 | { 8 | Task ExecuteAsync(Query query, string? continuationToken, CancellationToken cancellationToken); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/Services/IRequestOptionsBuilder.cs: -------------------------------------------------------------------------------- 1 | using Hamlet; 2 | using Microsoft.Azure.Cosmos; 3 | 4 | namespace CosmosDBStudio.Model.Services 5 | { 6 | public interface IRequestOptionsBuilder 7 | where TBuilder : IRequestOptionsBuilder 8 | where TOptions : RequestOptions 9 | { 10 | TOptions Build(); 11 | } 12 | 13 | public interface IQueryRequestOptionsBuilder : IRequestOptionsBuilder 14 | { 15 | IQueryRequestOptionsBuilder WithPartitionKey(Option partitionKey); 16 | IQueryRequestOptionsBuilder WithMaxItemCount(int? maxItemCount); 17 | } 18 | 19 | public interface IItemRequestOptionsBuilder : IRequestOptionsBuilder 20 | { 21 | IItemRequestOptionsBuilder IfMatch(string? eTag); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/Services/IScriptService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace CosmosDBStudio.Model.Services 5 | { 6 | public interface IScriptService 7 | { 8 | Task GetStoredProceduresAsync(CancellationToken cancellationToken); 9 | Task GetUserDefinedFunctionsAsync(CancellationToken cancellationToken); 10 | Task GetTriggersAsync(CancellationToken cancellationToken); 11 | 12 | Task CreateStoredProcedureAsync(CosmosStoredProcedure storedProcedure, CancellationToken cancellationToken); 13 | Task CreateUserDefinedFunctionAsync(CosmosUserDefinedFunction udf, CancellationToken cancellationToken); 14 | Task CreateTriggerAsync(CosmosTrigger trigger, CancellationToken cancellationToken); 15 | 16 | Task ReplaceStoredProcedureAsync(CosmosStoredProcedure storedProcedure, CancellationToken cancellationToken); 17 | Task ReplaceUserDefinedFunctionAsync(CosmosUserDefinedFunction udf, CancellationToken cancellationToken); 18 | Task ReplaceTriggerAsync(CosmosTrigger trigger, CancellationToken cancellationToken); 19 | 20 | Task DeleteStoredProcedureAsync(CosmosStoredProcedure storedProcedure, CancellationToken cancellationToken); 21 | Task DeleteUserDefinedFunctionAsync(CosmosUserDefinedFunction udf, CancellationToken cancellationToken); 22 | Task DeleteTriggerAsync(CosmosTrigger trigger, CancellationToken cancellationToken); 23 | 24 | Task ExecuteStoredProcedureAsync(CosmosStoredProcedure storedProcedure, object? partitionKey, object?[] parameters, CancellationToken cancellationToken); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/Services/Implementation/AccountContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Azure.Cosmos; 3 | 4 | namespace CosmosDBStudio.Model.Services.Implementation 5 | { 6 | public class AccountContext : IAccountContext 7 | { 8 | private readonly CosmosAccount _account; 9 | private readonly Func _clientGetter; 10 | 11 | public AccountContext(CosmosAccount account, Func clientGetter) 12 | { 13 | _account = account; 14 | _clientGetter = clientGetter; 15 | Databases = new DatabaseService(clientGetter); 16 | } 17 | 18 | public string AccountId => _account.Id; 19 | 20 | public string AccountName => _account.Name; 21 | 22 | public bool IsServerless => _account.IsServerless; 23 | 24 | public IDatabaseService Databases { get; } 25 | 26 | public IDatabaseContext GetDatabaseContext(CosmosDatabase database) 27 | { 28 | return new DatabaseContext(this, database.Id, () => _clientGetter().GetDatabase(database.Id)); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/Services/Implementation/AccountContextFactory.cs: -------------------------------------------------------------------------------- 1 | namespace CosmosDBStudio.Model.Services.Implementation 2 | { 3 | public class AccountContextFactory : IAccountContextFactory 4 | { 5 | private readonly IClientPool _clientPool; 6 | 7 | public AccountContextFactory(IClientPool clientPool) 8 | { 9 | _clientPool = clientPool; 10 | } 11 | 12 | public IAccountContext Create(CosmosAccount account) 13 | { 14 | return new AccountContext(account, () => _clientPool.GetClientForAccount(account)); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/Services/Implementation/ClientPool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using Microsoft.Azure.Cosmos; 4 | 5 | namespace CosmosDBStudio.Model.Services.Implementation 6 | { 7 | public class ClientPool : IClientPool 8 | { 9 | private readonly ConcurrentDictionary _clients; 10 | 11 | public ClientPool() 12 | { 13 | _clients = new ConcurrentDictionary(); 14 | } 15 | 16 | public CosmosClient GetClientForAccount(CosmosAccount account) 17 | { 18 | if (account is null) throw new ArgumentNullException(nameof(account)); 19 | 20 | return _clients.GetOrAdd(account.Id, _ => CreateClient(account)); 21 | } 22 | 23 | public void RemoveClientForAccount(CosmosAccount account) 24 | { 25 | if (account is null) throw new ArgumentNullException(nameof(account)); 26 | 27 | if (_clients.TryRemove(account.Id, out var client)) 28 | client.Dispose(); 29 | } 30 | 31 | private CosmosClient CreateClient(CosmosAccount account) 32 | { 33 | return new CosmosClient(account.Endpoint, account.Key); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/Services/Implementation/ContainerContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Microsoft.Azure.Cosmos; 5 | 6 | namespace CosmosDBStudio.Model.Services.Implementation 7 | { 8 | public class ContainerContext : IContainerContext 9 | { 10 | private readonly Func _containerGetter; 11 | 12 | public ContainerContext( 13 | IDatabaseContext databaseContext, 14 | string containerId, 15 | Func containerGetter, 16 | string partitionKeyPath) 17 | { 18 | DatabaseContext = databaseContext; 19 | ContainerId = containerId; 20 | _containerGetter = containerGetter; 21 | PartitionKeyPath = partitionKeyPath; 22 | PartitionKeyJsonPath = string.IsNullOrEmpty(partitionKeyPath) 23 | ? null 24 | : "$" + partitionKeyPath.Replace('/', '.'); 25 | Documents = new DocumentService(containerGetter); 26 | Query = new QueryService(containerGetter); 27 | Scripts = new ScriptService(containerGetter); 28 | } 29 | 30 | public IDatabaseContext DatabaseContext { get; } 31 | 32 | public string AccountId => DatabaseContext.AccountId; 33 | 34 | public string AccountName => DatabaseContext.AccountName; 35 | 36 | public string DatabaseId => DatabaseContext.DatabaseId; 37 | 38 | public string ContainerId { get; } 39 | 40 | public string? PartitionKeyPath { get; } 41 | public string? PartitionKeyJsonPath { get; } 42 | 43 | public IDocumentService Documents { get; } 44 | 45 | public IQueryService Query { get; } 46 | 47 | public IScriptService Scripts { get; } 48 | 49 | public Task GetContainerAsync(CancellationToken cancellationToken) => 50 | DatabaseContext.Containers.GetContainerAsync(ContainerId, cancellationToken); 51 | 52 | public Task GetThroughputAsync(CancellationToken cancellationToken) 53 | { 54 | var container = _containerGetter(); 55 | if (DatabaseContext.AccountContext.IsServerless) 56 | return Task.FromResult(default(int?)); 57 | return container.ReadThroughputAsync(cancellationToken); 58 | } 59 | 60 | public async Task SetThroughputAsync(int? throughput, CancellationToken cancellationToken) 61 | { 62 | var container = _containerGetter(); 63 | int? currentThroughput = await container.ReadThroughputAsync(cancellationToken); 64 | if (throughput.HasValue != currentThroughput.HasValue) 65 | return OperationResult.Forbidden; 66 | 67 | if (throughput.HasValue && throughput != currentThroughput) 68 | await container.ReplaceThroughputAsync(throughput.Value, cancellationToken: cancellationToken); 69 | 70 | return OperationResult.Success; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/Services/Implementation/DatabaseContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Microsoft.Azure.Cosmos; 5 | 6 | namespace CosmosDBStudio.Model.Services.Implementation 7 | { 8 | public class DatabaseContext : IDatabaseContext 9 | { 10 | private readonly Func _databaseGetter; 11 | 12 | public DatabaseContext(IAccountContext accountContext, string databaseId, Func databaseGetter) 13 | { 14 | AccountContext = accountContext; 15 | DatabaseId = databaseId; 16 | _databaseGetter = databaseGetter; 17 | Containers = new ContainerService(databaseGetter); 18 | } 19 | 20 | public IAccountContext AccountContext { get; } 21 | 22 | public string AccountId => AccountContext.AccountId; 23 | 24 | public string AccountName => AccountContext.AccountName; 25 | 26 | public string DatabaseId { get; } 27 | 28 | public IContainerService Containers { get; } 29 | 30 | public IContainerContext GetContainerContext(CosmosContainer container, CancellationToken cancellationToken) 31 | { 32 | return new ContainerContext(this, container.Id, () => _databaseGetter().GetContainer(container.Id), container.PartitionKeyPath); 33 | } 34 | 35 | public Task GetDatabaseAsync(CancellationToken cancellationToken) => 36 | AccountContext.Databases.GetDatabaseAsync(DatabaseId, cancellationToken); 37 | 38 | public Task GetThroughputAsync(CancellationToken cancellationToken) 39 | { 40 | var database = _databaseGetter(); 41 | if (AccountContext.IsServerless) 42 | return Task.FromResult(default(int?)); 43 | return database.ReadThroughputAsync(cancellationToken); 44 | } 45 | 46 | public async Task SetThroughputAsync(int? throughput, CancellationToken cancellationToken) 47 | { 48 | var database = _databaseGetter(); 49 | int? currentThroughput = await database.ReadThroughputAsync(cancellationToken); 50 | if (throughput.HasValue != currentThroughput.HasValue) 51 | return OperationResult.Forbidden; 52 | 53 | if (throughput.HasValue && throughput != currentThroughput) 54 | await database.ReplaceThroughputAsync(throughput.Value, cancellationToken: cancellationToken); 55 | 56 | return OperationResult.Success; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/Services/Implementation/DocumentService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Hamlet; 6 | using Microsoft.Azure.Cosmos; 7 | using Newtonsoft.Json.Linq; 8 | 9 | namespace CosmosDBStudio.Model.Services.Implementation 10 | { 11 | public class DocumentService : IDocumentService 12 | { 13 | private readonly Func _containerGetter; 14 | 15 | public DocumentService(Func containerGetter) 16 | { 17 | _containerGetter = containerGetter; 18 | } 19 | 20 | public async Task GetAsync(string id, Option partitionKey, CancellationToken cancellationToken) 21 | { 22 | try 23 | { 24 | var response = await _containerGetter().ReadItemAsync( 25 | id, 26 | PartitionKeyHelper.Create(partitionKey) ?? PartitionKey.None, 27 | null, 28 | cancellationToken); 29 | 30 | return response.Resource; 31 | } 32 | catch (CosmosException ex) when (ex.StatusCode == HttpStatusCode.NotFound) 33 | { 34 | return null; 35 | } 36 | } 37 | 38 | public async Task CreateAsync(JObject document, Option partitionKey, CancellationToken cancellationToken) 39 | { 40 | var response = await _containerGetter().CreateItemAsync( 41 | document, 42 | PartitionKeyHelper.Create(partitionKey), 43 | null, 44 | cancellationToken); 45 | 46 | return response.Resource; 47 | } 48 | 49 | public async Task ReplaceAsync(string id, JObject document, Option partitionKey, string? eTag, CancellationToken cancellationToken) 50 | { 51 | var options = new ItemRequestOptionsBuilder() 52 | .IfMatch(eTag) 53 | .Build(); 54 | 55 | var response = await _containerGetter().ReplaceItemAsync( 56 | document, 57 | id, 58 | PartitionKeyHelper.Create(partitionKey), 59 | options, 60 | cancellationToken); 61 | 62 | return response.Resource; 63 | } 64 | 65 | public async Task DeleteAsync(string id, Option partitionKey, string? eTag, CancellationToken cancellationToken) 66 | { 67 | var options = new ItemRequestOptionsBuilder() 68 | .IfMatch(eTag) 69 | .Build(); 70 | 71 | await _containerGetter().DeleteItemAsync( 72 | id, 73 | PartitionKeyHelper.Create(partitionKey) ?? PartitionKey.None, 74 | options, 75 | cancellationToken); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/Services/Implementation/ItemRequestOptionsBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.Cosmos; 2 | 3 | namespace CosmosDBStudio.Model.Services.Implementation 4 | { 5 | public class ItemRequestOptionsBuilder 6 | : RequestOptionsBuilderBase, 7 | IItemRequestOptionsBuilder 8 | { 9 | private string? _ifMatchEtag; 10 | 11 | public IItemRequestOptionsBuilder IfMatch(string? eTag) 12 | { 13 | _ifMatchEtag = eTag; 14 | return this; 15 | } 16 | 17 | protected override void Build(ItemRequestOptions options) 18 | { 19 | options.IfMatchEtag = _ifMatchEtag; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/Services/Implementation/PartitionKeyHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Hamlet; 3 | using Microsoft.Azure.Cosmos; 4 | 5 | namespace CosmosDBStudio.Model.Services.Implementation 6 | { 7 | public static class PartitionKeyHelper 8 | { 9 | public static PartitionKey? Create(Option partitionKey) 10 | { 11 | if (partitionKey.TryGetValue(out object? value)) 12 | return Create(value); 13 | 14 | return null; 15 | } 16 | 17 | public static PartitionKey Create(object? partitionKey) 18 | { 19 | return partitionKey switch 20 | { 21 | null => PartitionKey.Null, 22 | string s => new PartitionKey(s), 23 | double d => new PartitionKey(d), 24 | bool b => new PartitionKey(b), 25 | _ => throw new ArgumentException("Invalid partition key type") 26 | }; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/Services/Implementation/QueryRequestOptionsBuilder.cs: -------------------------------------------------------------------------------- 1 | using Hamlet; 2 | using Microsoft.Azure.Cosmos; 3 | 4 | namespace CosmosDBStudio.Model.Services.Implementation 5 | { 6 | public class QueryRequestOptionsBuilder 7 | : RequestOptionsBuilderBase, 8 | IQueryRequestOptionsBuilder 9 | { 10 | private Option _partitionKey; 11 | private int? _maxItemCount; 12 | 13 | public IQueryRequestOptionsBuilder WithPartitionKey(Option partitionKey) 14 | { 15 | _partitionKey = partitionKey; 16 | return this; 17 | } 18 | 19 | public IQueryRequestOptionsBuilder WithMaxItemCount(int? maxItemCount) 20 | { 21 | _maxItemCount = maxItemCount; 22 | return this; 23 | } 24 | 25 | protected override void Build(QueryRequestOptions options) 26 | { 27 | options.PartitionKey = PartitionKeyHelper.Create(_partitionKey); 28 | options.MaxItemCount = _maxItemCount; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/Services/Implementation/QueryService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using Microsoft.Azure.Cosmos; 8 | using Newtonsoft.Json.Linq; 9 | 10 | namespace CosmosDBStudio.Model.Services.Implementation 11 | { 12 | public class QueryService : IQueryService 13 | { 14 | private readonly Func _containerGetter; 15 | 16 | public QueryService(Func containerGetter) 17 | { 18 | _containerGetter = containerGetter; 19 | } 20 | 21 | public async Task ExecuteAsync(Query query, string? continuationToken, CancellationToken cancellationToken) 22 | { 23 | var queryDefinition = CreateQueryDefinition(query); 24 | var requestOptions = CreateRequestOptions(query); 25 | var iterator = _containerGetter().GetItemQueryIterator(queryDefinition, continuationToken, requestOptions); 26 | var result = new QueryResult(query); 27 | var stopwatch = new Stopwatch(); 28 | List? warnings = null; 29 | try 30 | { 31 | stopwatch.Start(); 32 | var response = await iterator.ReadNextAsync(); 33 | stopwatch.Stop(); 34 | result.RequestCharge += response.RequestCharge; 35 | try 36 | { 37 | result.ContinuationToken = response.ContinuationToken; 38 | } 39 | catch (Exception ex) 40 | { 41 | warnings ??= new List(); 42 | warnings.Add(ex.Message); 43 | } 44 | 45 | result.Items = response.ToList(); 46 | if (warnings != null) 47 | result.Warnings = warnings; 48 | } 49 | catch (Exception ex) 50 | { 51 | result.Error = ex; 52 | } 53 | finally 54 | { 55 | stopwatch.Stop(); 56 | } 57 | 58 | result.TimeElapsed = stopwatch.Elapsed; 59 | return result; 60 | } 61 | 62 | private static QueryDefinition CreateQueryDefinition(Query query) 63 | { 64 | var definition = new QueryDefinition(query.Sql); 65 | if (query.Parameters != null) 66 | { 67 | foreach (var (key, value) in query.Parameters) 68 | { 69 | definition = definition.WithParameter(key, value); 70 | } 71 | } 72 | 73 | return definition; 74 | } 75 | 76 | private static QueryRequestOptions CreateRequestOptions(Query query) 77 | { 78 | return new QueryRequestOptionsBuilder() 79 | .WithPartitionKey(query.PartitionKey) 80 | .WithMaxItemCount(100) 81 | .Build(); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/Services/Implementation/RequestOptionsBuilderBase.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.Cosmos; 2 | 3 | namespace CosmosDBStudio.Model.Services.Implementation 4 | { 5 | public abstract class RequestOptionsBuilderBase 6 | : IRequestOptionsBuilder 7 | where TBuilder : IRequestOptionsBuilder 8 | where TOptions : RequestOptions, new() 9 | { 10 | protected abstract void Build(TOptions options); 11 | 12 | public TOptions Build() 13 | { 14 | var options = new TOptions(); 15 | Build(options); 16 | return options; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/StoredProcedureResult.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using System; 3 | using System.Net; 4 | 5 | namespace CosmosDBStudio.Model 6 | { 7 | public class StoredProcedureResult 8 | { 9 | public HttpStatusCode StatusCode { get; set; } 10 | public JToken? Body { get; set; } 11 | public Exception? Error { get; set; } 12 | public TimeSpan TimeElapsed { get; set; } 13 | public double RequestCharge { get; set; } 14 | public string ScriptLog { get; set; } = string.Empty; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Model/Workspace.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CosmosDBStudio.Model 4 | { 5 | public class Workspace 6 | { 7 | public int UntitledCounter { get; set; } 8 | public IList QuerySheets { get; set; } = new List(); 9 | } 10 | 11 | public class WorkspaceQuerySheet 12 | { 13 | public string Title { get; set; } = string.Empty; 14 | public string? SavedPath { get; set; } 15 | public string? TempPath { get; set; } 16 | public bool HasChanges { get; set; } 17 | public bool IsCurrent { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Util/CosmosDBStudio.Util.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Util/Extensions/JTokenExtensions.cs: -------------------------------------------------------------------------------- 1 | using Hamlet; 2 | using Newtonsoft.Json.Linq; 3 | 4 | namespace CosmosDBStudio.Util.Extensions 5 | { 6 | public static class JTokenExtensions 7 | { 8 | public static Option ExtractScalar(this JToken document, string jsonPath) 9 | { 10 | var token = document.SelectToken(jsonPath); 11 | return token is JValue valueToken 12 | ? Option.Some((object?)valueToken.Value) 13 | : Option.None(); 14 | } 15 | 16 | public static bool IsRawDocument(this JToken document) 17 | { 18 | // If the document has all system properties, it's probably 19 | // a raw document 20 | return document is JObject obj && 21 | obj.TryGetValue("id", out _) && 22 | obj.TryGetValue("_rid", out _) && 23 | obj.TryGetValue("_self", out _) && 24 | obj.TryGetValue("_etag", out _) && 25 | obj.TryGetValue("_ts", out _); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Util/Extensions/LinkedListExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CosmosDBStudio.Util.Extensions 4 | { 5 | public static class LinkedListExtensions 6 | { 7 | public static IEnumerable> Nodes(this LinkedList linkedList) 8 | { 9 | var node = linkedList.First; 10 | while (node != null) 11 | { 12 | yield return node; 13 | node = node.Next; 14 | } 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Util/Extensions/ObservableCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | 3 | namespace CosmosDBStudio.Util.Extensions 4 | { 5 | public static class ObservableCollectionExtensions 6 | { 7 | public static void PushMRU(this ObservableCollection mruList, T value, int maxMRUCount) 8 | { 9 | int index = mruList.IndexOf(value); 10 | if (index == 0) 11 | { 12 | return; 13 | } 14 | else if (index > 0) 15 | { 16 | mruList.Move(index, 0); 17 | } 18 | else 19 | { 20 | mruList.Insert(0, value); 21 | } 22 | 23 | while (mruList.Count > maxMRUCount) 24 | { 25 | mruList.RemoveAt(mruList.Count - 1); 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Util/Extensions/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace CosmosDBStudio.Util.Extensions 5 | { 6 | public static class ServiceCollectionExtensions 7 | { 8 | public static IServiceCollection AddLazyResolution(this IServiceCollection services) 9 | { 10 | return services.AddTransient( 11 | typeof(Lazy<>), 12 | typeof(LazilyResolved<>)); 13 | } 14 | 15 | private class LazilyResolved : Lazy 16 | where T : notnull 17 | { 18 | public LazilyResolved(IServiceProvider serviceProvider) 19 | : base(serviceProvider.GetRequiredService) 20 | { 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.Util/Helpers/JsonHelper.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Hamlet; 3 | using Newtonsoft.Json; 4 | using Newtonsoft.Json.Linq; 5 | 6 | namespace CosmosDBStudio.Util.Helpers 7 | { 8 | public class JsonHelper 9 | { 10 | public static bool TryParseJsonValue(string? rawJson, out object? value) 11 | { 12 | if (string.IsNullOrEmpty(rawJson)) 13 | { 14 | value = null; 15 | return false; 16 | } 17 | 18 | try 19 | { 20 | using var tReader = new StringReader(rawJson); 21 | using var jReader = new JsonTextReader(tReader) 22 | { 23 | DateParseHandling = DateParseHandling.None 24 | }; 25 | 26 | var token = JValue.ReadFrom(jReader); 27 | value = token.ToObject(); 28 | return true; 29 | } 30 | catch 31 | { 32 | value = null; 33 | return false; 34 | } 35 | } 36 | 37 | public static Option TryParseJsonValue(string? rawJson) 38 | { 39 | return TryParseJsonValue(rawJson, out var value) 40 | ? Option.Some(value) 41 | : Option.None(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/AboutViewModel.cs: -------------------------------------------------------------------------------- 1 | using EssentialMVVM; 2 | using System; 3 | using System.Diagnostics; 4 | using System.Windows.Input; 5 | using CosmosDBStudio.Model.Services; 6 | using CosmosDBStudio.ViewModel.Dialogs; 7 | 8 | namespace CosmosDBStudio.ViewModel 9 | { 10 | public class AboutViewModel : DialogViewModelBase 11 | { 12 | public AboutViewModel(IApplication application) 13 | { 14 | AddOkButton(); 15 | try 16 | { 17 | var info = application.GetVersionInfo(); 18 | ProductName = info.ProductName; 19 | Version = info.Version; 20 | Author = info.Author; 21 | } 22 | catch(InvalidOperationException) 23 | { 24 | ProductName = "Cosmos DB Studio"; 25 | Version = "X.X.X.X"; 26 | Author = "Thomas Levesque"; 27 | } 28 | Title = "About " + ProductName; 29 | } 30 | 31 | public string ProductName { get; } 32 | public string Version { get; } 33 | public string Author { get; } 34 | 35 | private DelegateCommand? _openWebsiteCommand; 36 | public ICommand OpenWebsiteCommand => _openWebsiteCommand ??= new DelegateCommand(OpenWebsite); 37 | 38 | private void OpenWebsite() 39 | { 40 | Process.Start(new ProcessStartInfo("https://github.com/thomaslevesque/CosmosDBStudio") 41 | { 42 | UseShellExecute = true 43 | }); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/CommandViewModel.cs: -------------------------------------------------------------------------------- 1 | using EssentialMVVM; 2 | using System.Windows.Input; 3 | 4 | namespace CosmosDBStudio.ViewModel 5 | { 6 | public class CommandViewModel : BindableBase 7 | { 8 | private CommandViewModel() { } 9 | 10 | public CommandViewModel(string text, ICommand command, object? commandParameter = null, bool isDefault = false) 11 | { 12 | _text = text; 13 | _command = command; 14 | _commandParameter = commandParameter; 15 | _isDefault = isDefault; 16 | } 17 | 18 | private string? _text; 19 | public string? Text 20 | { 21 | get => _text; 22 | set => Set(ref _text, value); 23 | } 24 | 25 | private ICommand? _command; 26 | public ICommand? Command 27 | { 28 | get => _command; 29 | set => Set(ref _command, value); 30 | } 31 | 32 | private object? _commandParameter; 33 | public object? CommandParameter 34 | { 35 | get => _commandParameter; 36 | set => Set(ref _commandParameter, value); 37 | } 38 | 39 | public bool IsSeparator => Text is null; 40 | 41 | private bool _isDefault; 42 | public bool IsDefault 43 | { 44 | get => _isDefault; 45 | set => Set(ref _isDefault, value); 46 | } 47 | 48 | public static CommandViewModel Separator() => new CommandViewModel(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/Commands/CommandExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Input; 2 | using EssentialMVVM; 3 | 4 | namespace CosmosDBStudio.ViewModel.Commands 5 | { 6 | public static class CommandExtensions 7 | { 8 | public static DelegateCommand WithParameter(this ICommand command, object parameter) 9 | { 10 | return new DelegateCommand(() => command.Execute(parameter), () => command.CanExecute(parameter)); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/Commands/ScriptCommands.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Threading.Tasks; 3 | using System.Windows.Input; 4 | using CosmosDBStudio.Model; 5 | using CosmosDBStudio.Model.Services; 6 | using CosmosDBStudio.ViewModel.Messages; 7 | using CosmosDBStudio.ViewModel.Services; 8 | using CosmosDBStudio.ViewModel.TreeNodes; 9 | using EssentialMVVM; 10 | 11 | namespace CosmosDBStudio.ViewModel.Commands 12 | { 13 | public class ScriptCommands 14 | where TScript : ICosmosScript, new() 15 | { 16 | private readonly IMessenger _messenger; 17 | private readonly IDialogService _dialogService; 18 | 19 | public ScriptCommands(IMessenger messenger, IDialogService dialogService) 20 | { 21 | _messenger = messenger; 22 | _dialogService = dialogService; 23 | } 24 | 25 | #region Open 26 | 27 | private DelegateCommand>? _openCommand; 28 | public ICommand OpenCommand => _openCommand ??= new DelegateCommand>(Open); 29 | 30 | private void Open(ScriptNodeViewModel scriptVm) 31 | { 32 | _messenger.Publish(new OpenScriptMessage(scriptVm.Context, scriptVm.Script)); 33 | } 34 | 35 | #endregion 36 | 37 | #region Create 38 | 39 | private DelegateCommand? _createCommand; 40 | public ICommand CreateCommand => _createCommand ??= new DelegateCommand(Create); 41 | 42 | private void Create(ScriptFolderNodeViewModel parent) 43 | { 44 | var result = _dialogService.TextPrompt("Enter id for new item"); 45 | if (!result.TryGetValue(out var id)) 46 | { 47 | return; 48 | } 49 | 50 | if (parent.Children.OfType>().Any(c => c.Script.Id == id)) 51 | { 52 | _dialogService.ShowError("Another item with the same name already exists"); 53 | return; 54 | } 55 | 56 | var script = new TScript(); 57 | script.Id = id; 58 | script.Body = $@"function {id} () {{ 59 | 60 | }}"; 61 | 62 | _messenger.Publish(new OpenScriptMessage(parent.Context, script)); 63 | } 64 | 65 | #endregion 66 | 67 | #region Delete 68 | 69 | private AsyncDelegateCommand>? _deleteCommand; 70 | public ICommand DeleteCommand => _deleteCommand ??= new AsyncDelegateCommand>(DeleteAsync); 71 | 72 | private async Task DeleteAsync(ScriptNodeViewModel scriptVm) 73 | { 74 | if (!_dialogService.Confirm($"Are you sure you want to delete {scriptVm.Description} '{scriptVm.Script.Id}'?")) 75 | return; 76 | 77 | await scriptVm.DeleteAsync(); 78 | 79 | scriptVm.Parent?.ReloadChildren(); 80 | } 81 | 82 | #endregion 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/CosmosDBStudio.ViewModel.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/Dialogs/ContainerPickerViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.ObjectModel; 3 | using System.Windows.Input; 4 | using CosmosDBStudio.Model; 5 | using CosmosDBStudio.Model.Services; 6 | using CosmosDBStudio.ViewModel.TreeNodes; 7 | using EssentialMVVM; 8 | 9 | namespace CosmosDBStudio.ViewModel.Dialogs 10 | { 11 | public class ContainerPickerViewModel : DialogViewModelBase 12 | { 13 | private readonly IAccountContextFactory _accountContextFactory; 14 | private readonly IViewModelFactory _viewModelFactory; 15 | private readonly IAccountDirectory _accountDirectory; 16 | 17 | public ContainerPickerViewModel( 18 | IAccountContextFactory accountContextFactory, 19 | IViewModelFactory viewModelFactory, 20 | IAccountDirectory accountDirectory) 21 | { 22 | _accountContextFactory = accountContextFactory; 23 | _viewModelFactory = viewModelFactory; 24 | _accountDirectory = accountDirectory; 25 | AddCancelButton(); 26 | AddOkButton(button => 27 | { 28 | button.Text = "Select"; 29 | button.Command = SelectCommand; 30 | }); 31 | 32 | RootNodes = new ObservableCollection(); 33 | Title = "Pick a container"; 34 | LoadAccounts(); 35 | } 36 | 37 | private void LoadAccounts() 38 | { 39 | var nodes = _accountDirectory.GetRootNodes(); 40 | foreach (var node in nodes) 41 | { 42 | var vm = node switch 43 | { 44 | CosmosAccount account => 45 | (TreeNodeViewModel)_viewModelFactory.CreateAccountNode( 46 | account, 47 | _accountContextFactory.Create(account), 48 | null), 49 | CosmosAccountFolder folder => (TreeNodeViewModel)_viewModelFactory.CreateAccountFolderNode(folder, null), 50 | _ => throw new Exception("Invalid node type") 51 | }; 52 | 53 | RootNodes.Add(vm); 54 | } 55 | } 56 | 57 | public ObservableCollection RootNodes { get; } 58 | 59 | private object? _selectedItem; 60 | public object? SelectedItem 61 | { 62 | get => _selectedItem; 63 | set => Set(ref _selectedItem, value) 64 | .AndNotifyPropertyChanged(nameof(SelectedContainer)) 65 | .AndRaiseCanExecuteChanged(_selectCommand); 66 | } 67 | 68 | public ContainerNodeViewModel? SelectedContainer => SelectedItem as ContainerNodeViewModel; 69 | 70 | private DelegateCommand? _selectCommand; 71 | public ICommand SelectCommand => _selectCommand ??= new DelegateCommand(Select, CanSelect); 72 | 73 | private void Select() => Close(true); 74 | 75 | private bool CanSelect() => !(SelectedContainer is null); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/Dialogs/DialogButton.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Input; 2 | 3 | namespace CosmosDBStudio.ViewModel.Dialogs 4 | { 5 | public class DialogButton 6 | { 7 | public string Text { get; set; } = string.Empty; 8 | public bool? DialogResult { get; set; } 9 | public bool IsDefault { get; set; } 10 | public bool IsCancel { get; set; } 11 | public ICommand? Command { get; set; } 12 | public object? CommandParameter { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/Dialogs/DialogClosingEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace CosmosDBStudio.ViewModel.Dialogs 4 | { 5 | public class DialogClosingEventArgs : CancelEventArgs 6 | { 7 | public DialogClosingEventArgs(bool? dialogResult) 8 | { 9 | DialogResult = dialogResult; 10 | } 11 | 12 | public bool? DialogResult { get; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/Dialogs/DialogViewModelBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using EssentialMVVM; 5 | 6 | namespace CosmosDBStudio.ViewModel.Dialogs 7 | { 8 | public class DialogViewModelBase : BindableBase, IDialogViewModel 9 | { 10 | private readonly ObservableCollection _buttons = new ObservableCollection(); 11 | 12 | private string _title = string.Empty; 13 | public string Title 14 | { 15 | get => _title; 16 | protected set => Set(ref _title, value); 17 | } 18 | 19 | public IEnumerable Buttons => _buttons; 20 | 21 | public bool HasButtons => _buttons.Count > 0; 22 | 23 | public event EventHandler? CloseRequested; 24 | 25 | protected void AddButton(DialogButton button) 26 | { 27 | _buttons.Add(button); 28 | } 29 | 30 | protected void RemoveButton(DialogButton button) 31 | { 32 | _buttons.Remove(button); 33 | } 34 | 35 | protected void AddOkButton(Action? customize = null) 36 | { 37 | var button = new DialogButton 38 | { 39 | Text = "OK", 40 | IsDefault = true, 41 | DialogResult = true 42 | }; 43 | 44 | customize?.Invoke(button); 45 | AddButton(button); 46 | } 47 | 48 | protected void AddCancelButton(Action? customize = null) 49 | { 50 | var button = new DialogButton 51 | { 52 | Text = "Cancel", 53 | IsCancel = true, 54 | DialogResult = false 55 | }; 56 | 57 | customize?.Invoke(button); 58 | AddButton(button); 59 | } 60 | 61 | protected void Close(bool? dialogResult) 62 | { 63 | CloseRequested?.Invoke(this, dialogResult); 64 | } 65 | 66 | public virtual void OnClosed(bool? result) 67 | { 68 | } 69 | 70 | public virtual void OnClosing(DialogClosingEventArgs args) 71 | { 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/Dialogs/IDialogViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CosmosDBStudio.ViewModel.Dialogs 5 | { 6 | public interface IDialogViewModel 7 | { 8 | string Title { get; } 9 | IEnumerable Buttons { get; } 10 | bool HasButtons { get; } 11 | event EventHandler CloseRequested; 12 | void OnClosing(DialogClosingEventArgs args); 13 | void OnClosed(bool? result); 14 | } 15 | 16 | public interface ISizableDialog 17 | { 18 | double Width { get; set; } 19 | double Height { get; set; } 20 | bool IsResizable { get; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/Dialogs/TextPromptViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Input; 2 | using EssentialMVVM; 3 | 4 | namespace CosmosDBStudio.ViewModel.Dialogs 5 | { 6 | public class TextPromptViewModel : DialogViewModelBase 7 | { 8 | public TextPromptViewModel(string prompt, string? text) 9 | { 10 | _prompt = prompt; 11 | _text = text ?? string.Empty; 12 | AddOkButton(button => button.Command = SubmitCommand); 13 | AddCancelButton(); 14 | } 15 | 16 | private string _text; 17 | public string Text 18 | { 19 | get => _text; 20 | set => Set(ref _text, value) 21 | .AndRaiseCanExecuteChanged(_submitCommand); 22 | } 23 | 24 | private string _prompt; 25 | public string Prompt 26 | { 27 | get => _prompt; 28 | set => Set(ref _prompt, value); 29 | } 30 | 31 | private DelegateCommand? _submitCommand; 32 | public ICommand SubmitCommand => _submitCommand ??= new DelegateCommand(Submit, CanSubmit); 33 | 34 | private void Submit() => Close(true); 35 | 36 | private bool CanSubmit() => !string.IsNullOrEmpty(Text); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/EditorTabs/ParameterViewModelBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.ObjectModel; 3 | using System.Windows.Input; 4 | using CosmosDBStudio.Util.Helpers; 5 | using EssentialMVVM; 6 | 7 | namespace CosmosDBStudio.ViewModel.EditorTabs 8 | { 9 | public abstract class ParameterViewModelBase : BindableBase 10 | where TViewModel : ParameterViewModelBase 11 | { 12 | public ParameterViewModelBase() 13 | { 14 | MRU = new ObservableCollection(); 15 | Errors = new ViewModelValidator((TViewModel)this); 16 | Errors.AddValidator(vm => vm.RawValue, ValidateValue); 17 | } 18 | 19 | private string? _rawValue; 20 | public string? RawValue 21 | { 22 | get => _rawValue; 23 | set => Set(ref _rawValue, value) 24 | .AndExecute(DataChanged); 25 | } 26 | 27 | public ViewModelValidator Errors { get; } 28 | 29 | public ObservableCollection MRU { get; } 30 | 31 | private bool _isPlaceholder; 32 | public bool IsPlaceholder 33 | { 34 | get => _isPlaceholder; 35 | set => Set(ref _isPlaceholder, value); 36 | } 37 | 38 | public event EventHandler? Created; 39 | public event EventHandler? DeleteRequested; 40 | 41 | private DelegateCommand? _deleteCommand; 42 | public ICommand DeleteCommand => _deleteCommand ??= new DelegateCommand(Delete, CanDelete); 43 | 44 | private void Delete() 45 | { 46 | DeleteRequested?.Invoke(this, EventArgs.Empty); 47 | } 48 | 49 | private bool CanDelete() => !IsPlaceholder; 50 | 51 | protected void DataChanged() 52 | { 53 | if (IsPlaceholder && HasData()) 54 | { 55 | IsPlaceholder = false; 56 | Created?.Invoke(this, EventArgs.Empty); 57 | } 58 | 59 | Errors?.Refresh(); 60 | _deleteCommand?.RaiseCanExecuteChanged(); 61 | OnChanged(); 62 | } 63 | 64 | protected virtual bool HasData() => !string.IsNullOrEmpty(RawValue); 65 | 66 | private string ValidateValue(string? rawValue) 67 | { 68 | if (IsPlaceholder) 69 | return string.Empty; 70 | 71 | if (!JsonHelper.TryParseJsonValue(rawValue, out _)) 72 | return "Invalid JSON value"; 73 | 74 | return string.Empty; 75 | } 76 | 77 | public bool TryParseValue(out object? value) => JsonHelper.TryParseJsonValue(RawValue, out value); 78 | 79 | public event EventHandler? Changed; 80 | 81 | protected virtual void OnChanged() 82 | { 83 | Changed?.Invoke(this, EventArgs.Empty); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/EditorTabs/ParametersViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.ObjectModel; 3 | using EssentialMVVM; 4 | 5 | namespace CosmosDBStudio.ViewModel.EditorTabs 6 | { 7 | public class ParametersViewModel : BindableBase 8 | where T : ParameterViewModelBase, new() 9 | { 10 | public ParametersViewModel() 11 | { 12 | Parameters = new ObservableCollection(); 13 | } 14 | 15 | public ObservableCollection Parameters { get; } 16 | 17 | public void AddParameter(T parameter) 18 | { 19 | parameter.DeleteRequested += OnParameterDeleteRequested; 20 | Parameters.Add(parameter); 21 | if (parameter.IsPlaceholder) 22 | { 23 | parameter.Created += OnParameterCreated; 24 | } 25 | else 26 | { 27 | parameter.DeleteRequested += OnParameterDeleteRequested; 28 | parameter.Changed += OnParameterChanged; 29 | } 30 | } 31 | 32 | public void AddPlaceholder() 33 | { 34 | var placeholder = new T { IsPlaceholder = true }; 35 | placeholder.Created += OnParameterCreated; 36 | Parameters.Add(placeholder); 37 | } 38 | 39 | public event EventHandler? Changed; 40 | 41 | private void OnChanged() 42 | { 43 | Changed?.Invoke(this, EventArgs.Empty); 44 | } 45 | 46 | private void OnParameterChanged(object? sender, EventArgs e) 47 | { 48 | OnChanged(); 49 | } 50 | 51 | private void OnParameterCreated(object? sender, EventArgs _) 52 | { 53 | if (sender is T parameter) 54 | { 55 | parameter.Created -= OnParameterCreated; 56 | parameter.DeleteRequested += OnParameterDeleteRequested; 57 | parameter.Changed += OnParameterChanged; 58 | AddPlaceholder(); 59 | OnChanged(); 60 | } 61 | } 62 | 63 | private void OnParameterDeleteRequested(object? sender, EventArgs e) 64 | { 65 | if (sender is T parameter) 66 | { 67 | parameter.DeleteRequested -= OnParameterDeleteRequested; 68 | parameter.Changed -= OnParameterChanged; 69 | Parameters.Remove(parameter); 70 | OnChanged(); 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/EditorTabs/Queries/EmptyResultItemPlaceholderViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace CosmosDBStudio.ViewModel.EditorTabs.Queries 2 | { 3 | public class EmptyResultItemPlaceholderViewModel : ResultItemViewModel 4 | { 5 | public override string DisplayValue => "(no results)"; 6 | 7 | public override object? PartitionKey => null; 8 | 9 | public override string Text 10 | { 11 | get => DisplayValue; 12 | set { } 13 | } 14 | 15 | public override bool IsJson => false; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/EditorTabs/Queries/ErrorItemPlaceholderViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace CosmosDBStudio.ViewModel.EditorTabs.Queries 2 | { 3 | public class ErrorItemPlaceholderViewModel : ResultItemViewModel 4 | { 5 | public override string DisplayValue => "(error)"; 6 | 7 | public override object? PartitionKey => null; 8 | 9 | public override string Text 10 | { 11 | get => "(error)"; 12 | set { } 13 | } 14 | 15 | public override bool IsJson => false; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/EditorTabs/Queries/NotRunQueryResultViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CosmosDBStudio.ViewModel.EditorTabs.Queries 5 | { 6 | public class NotRunQueryResultViewModel : QueryResultViewModelBase 7 | { 8 | public override IReadOnlyList Items => Array.Empty(); 9 | 10 | public override bool IsJson => false; 11 | 12 | public override string Text => string.Empty; 13 | 14 | public override string? Error => null; 15 | 16 | public override ResultItemViewModel? SelectedItem { get; set; } 17 | } 18 | } -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/EditorTabs/Queries/QueryParameterViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | 3 | namespace CosmosDBStudio.ViewModel.EditorTabs.Queries 4 | { 5 | public class QueryParameterViewModel : ParameterViewModelBase 6 | { 7 | public QueryParameterViewModel() 8 | { 9 | Errors.AddValidator(vm => vm.Name, ValidateName); 10 | } 11 | 12 | private string ValidateName(string? name) 13 | { 14 | if (IsPlaceholder) 15 | return string.Empty; 16 | 17 | if (string.IsNullOrEmpty(name)) 18 | return "Name must be set"; 19 | 20 | if (!Regex.IsMatch(name, @"^@?[a-zA-Z_][a-zA-Z0-9_]*$")) 21 | return "Invalid characters in name"; 22 | 23 | return string.Empty; 24 | } 25 | 26 | private string? _name; 27 | public string? Name 28 | { 29 | get => _name; 30 | set => Set(ref _name, value) 31 | .AndExecute(DataChanged); 32 | } 33 | 34 | protected override bool HasData() => base.HasData() || !string.IsNullOrEmpty(Name); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/EditorTabs/Queries/QueryResultViewModelBase.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using EssentialMVVM; 3 | 4 | namespace CosmosDBStudio.ViewModel.EditorTabs.Queries 5 | { 6 | public abstract class QueryResultViewModelBase : BindableBase 7 | { 8 | public abstract IReadOnlyList Items { get; } 9 | public abstract bool IsJson { get; } 10 | public abstract string Text { get; } 11 | public abstract string? Error { get; } 12 | public abstract ResultItemViewModel? SelectedItem { get; set; } 13 | 14 | 15 | private ResultTab _selectedTab; 16 | public ResultTab SelectedTab 17 | { 18 | get => _selectedTab; 19 | set => Set(ref _selectedTab, value) 20 | .AndNotifyPropertyChanged(nameof(IsItemsTabSelected)) 21 | .AndNotifyPropertyChanged(nameof(IsRawTabSelected)) 22 | .AndNotifyPropertyChanged(nameof(IsErrorTabSelected)); 23 | } 24 | 25 | public bool IsItemsTabSelected => SelectedTab == ResultTab.Items; 26 | public bool IsRawTabSelected => SelectedTab == ResultTab.Raw; 27 | public bool IsErrorTabSelected => SelectedTab == ResultTab.Error; 28 | 29 | public enum ResultTab 30 | { 31 | Items = 0, 32 | Raw = 1, 33 | Error = 2 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/EditorTabs/Queries/ResultItemViewModel.cs: -------------------------------------------------------------------------------- 1 | using EssentialMVVM; 2 | 3 | namespace CosmosDBStudio.ViewModel.EditorTabs.Queries 4 | { 5 | public abstract class ResultItemViewModel : BindableBase 6 | { 7 | public abstract string DisplayValue { get; } 8 | public abstract object? PartitionKey { get; } 9 | public abstract string Text { get; set; } 10 | public abstract bool IsJson { get; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/EditorTabs/StoredProcedureResultViewModel.cs: -------------------------------------------------------------------------------- 1 | using CosmosDBStudio.Model; 2 | using EssentialMVVM; 3 | using Newtonsoft.Json; 4 | 5 | namespace CosmosDBStudio.ViewModel.EditorTabs 6 | { 7 | public class StoredProcedureResultViewModel : BindableBase 8 | { 9 | public StoredProcedureResultViewModel(StoredProcedureResult result) 10 | { 11 | Text = result.Body?.ToString(Formatting.Indented) ?? string.Empty; 12 | ScriptLog = result.ScriptLog; 13 | Error = result.Error?.Message ?? string.Empty; 14 | if (result.Error != null) 15 | SelectedTab = ResultTab.Error; 16 | } 17 | 18 | public string Text { get; } 19 | public string ScriptLog { get; } 20 | public string Error { get; set; } 21 | 22 | private ResultTab _selectedTab; 23 | public ResultTab SelectedTab 24 | { 25 | get => _selectedTab; 26 | set => Set(ref _selectedTab, value) 27 | .AndNotifyPropertyChanged(nameof(IsScriptLogTabSelected)) 28 | .AndNotifyPropertyChanged(nameof(IsRawTabSelected)) 29 | .AndNotifyPropertyChanged(nameof(IsErrorTabSelected)); 30 | } 31 | 32 | public bool IsRawTabSelected => SelectedTab == ResultTab.Raw; 33 | public bool IsScriptLogTabSelected => SelectedTab == ResultTab.ScriptLog; 34 | public bool IsErrorTabSelected => SelectedTab == ResultTab.Error; 35 | 36 | public enum ResultTab 37 | { 38 | Raw = 0, 39 | ScriptLog = 1, 40 | Error = 2 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/EditorTabs/TabViewModelBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Input; 3 | using EssentialMVVM; 4 | 5 | namespace CosmosDBStudio.ViewModel.EditorTabs 6 | { 7 | public abstract class TabViewModelBase : BindableBase 8 | { 9 | public abstract string Title { get; } 10 | 11 | public abstract string Description { get; } 12 | 13 | private DelegateCommand? _closeCommand; 14 | public ICommand CloseCommand => _closeCommand ??= new DelegateCommand(Close); 15 | 16 | private void Close() 17 | { 18 | CloseRequested?.Invoke(this, EventArgs.Empty); 19 | } 20 | 21 | public event EventHandler? CloseRequested; 22 | 23 | public abstract bool HasChanges { get; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/EditorTabs/TriggerEditorViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using CosmosDBStudio.Model; 3 | using CosmosDBStudio.Model.Services; 4 | using Microsoft.Azure.Cosmos.Scripts; 5 | 6 | namespace CosmosDBStudio.ViewModel.EditorTabs 7 | { 8 | public class TriggerEditorViewModel : ScriptEditorViewModelBase 9 | { 10 | public TriggerEditorViewModel(CosmosTrigger trigger, IContainerContext containerContext) 11 | : base(trigger, containerContext) 12 | { 13 | Type = trigger.Type; 14 | Operation = trigger.Operation; 15 | } 16 | 17 | public override string Description => "trigger"; 18 | 19 | protected override void ApplyChanges(CosmosTrigger script) 20 | { 21 | base.ApplyChanges(script); 22 | script.Type = Type; 23 | script.Operation = Operation; 24 | } 25 | 26 | protected override void Revert() 27 | { 28 | base.Revert(); 29 | Type = Script.Type; 30 | Operation = Script.Operation; 31 | } 32 | 33 | protected override Task CreateScriptAsync(CosmosTrigger script) => 34 | ContainerContext.Scripts.CreateTriggerAsync(script, default); 35 | 36 | protected override Task ReplaceScriptAsync(CosmosTrigger script) => 37 | ContainerContext.Scripts.ReplaceTriggerAsync(script, default); 38 | 39 | private TriggerOperation _operation; 40 | public TriggerOperation Operation 41 | { 42 | get => _operation; 43 | set => Set(ref _operation, value) 44 | .AndNotifyPropertyChanged(nameof(HasChanges)) 45 | .AndExecute(RefreshCommands); 46 | } 47 | 48 | private TriggerType _type; 49 | public TriggerType Type 50 | { 51 | get => _type; 52 | set => Set(ref _type, value) 53 | .AndNotifyPropertyChanged(nameof(HasChanges)) 54 | .AndExecute(RefreshCommands); 55 | } 56 | 57 | public override bool HasChanges => base.HasChanges 58 | || Type != Script.Type 59 | || Operation != Script.Operation; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/EditorTabs/UserDefinedFunctionEditorViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using CosmosDBStudio.Model; 3 | using CosmosDBStudio.Model.Services; 4 | 5 | namespace CosmosDBStudio.ViewModel.EditorTabs 6 | { 7 | public class UserDefinedFunctionEditorViewModel : ScriptEditorViewModelBase 8 | { 9 | public UserDefinedFunctionEditorViewModel(CosmosUserDefinedFunction udf, IContainerContext containerContext) 10 | : base(udf, containerContext) 11 | { 12 | } 13 | 14 | public override string Description => "user-defined function"; 15 | 16 | protected override Task CreateScriptAsync(CosmosUserDefinedFunction script) => 17 | ContainerContext.Scripts.CreateUserDefinedFunctionAsync(script, default); 18 | 19 | protected override Task ReplaceScriptAsync(CosmosUserDefinedFunction script) => 20 | ContainerContext.Scripts.ReplaceUserDefinedFunctionAsync(script, default); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/ISaveable.cs: -------------------------------------------------------------------------------- 1 | namespace CosmosDBStudio.ViewModel 2 | { 3 | public interface ISaveable 4 | { 5 | void Save(string path); 6 | string? FilePath { get; } 7 | bool HasChanges { get; } 8 | string FileFilter { get; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/Messages/AccountAddedMessage.cs: -------------------------------------------------------------------------------- 1 | using CosmosDBStudio.Model; 2 | 3 | namespace CosmosDBStudio.ViewModel.Messages 4 | { 5 | public class AccountAddedMessage 6 | { 7 | public AccountAddedMessage(CosmosAccount account) 8 | { 9 | Account = account; 10 | } 11 | 12 | public CosmosAccount Account { get; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/Messages/AccountEditedMessage.cs: -------------------------------------------------------------------------------- 1 | using CosmosDBStudio.Model; 2 | 3 | namespace CosmosDBStudio.ViewModel.Messages 4 | { 5 | public class AccountEditedMessage 6 | { 7 | public AccountEditedMessage(CosmosAccount account, CosmosAccount oldAccount) 8 | { 9 | Account = account; 10 | OldAccount = oldAccount; 11 | } 12 | 13 | public CosmosAccount Account { get; } 14 | public CosmosAccount OldAccount { get; } 15 | public bool CredentialsChanged => Account.Endpoint != OldAccount.Endpoint || Account.Key != OldAccount.Key; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/Messages/AccountRemovedMessage.cs: -------------------------------------------------------------------------------- 1 | using CosmosDBStudio.Model; 2 | 3 | namespace CosmosDBStudio.ViewModel.Messages 4 | { 5 | public class AccountRemovedMessage 6 | { 7 | public AccountRemovedMessage(CosmosAccount account) 8 | { 9 | Account = account; 10 | } 11 | 12 | public CosmosAccount Account { get; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/Messages/ContainerCreatedMessage.cs: -------------------------------------------------------------------------------- 1 | using CosmosDBStudio.Model; 2 | using CosmosDBStudio.Model.Services; 3 | 4 | namespace CosmosDBStudio.ViewModel.Messages 5 | { 6 | public class ContainerCreatedMessage 7 | { 8 | public ContainerCreatedMessage(IDatabaseContext context, CosmosContainer container) 9 | { 10 | Context = context; 11 | Container = container; 12 | } 13 | 14 | public IDatabaseContext Context { get; } 15 | public CosmosContainer Container { get; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/Messages/ContainerDeletedMessage.cs: -------------------------------------------------------------------------------- 1 | using CosmosDBStudio.Model; 2 | using CosmosDBStudio.Model.Services; 3 | 4 | namespace CosmosDBStudio.ViewModel.Messages 5 | { 6 | public class ContainerDeletedMessage 7 | { 8 | public ContainerDeletedMessage(IDatabaseContext context, CosmosContainer container) 9 | { 10 | Context = context; 11 | Container = container; 12 | } 13 | 14 | public IDatabaseContext Context { get; } 15 | public CosmosContainer Container { get; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/Messages/DatabaseCreatedMessage.cs: -------------------------------------------------------------------------------- 1 | using CosmosDBStudio.Model; 2 | using CosmosDBStudio.Model.Services; 3 | 4 | namespace CosmosDBStudio.ViewModel.Messages 5 | { 6 | public class DatabaseCreatedMessage 7 | { 8 | public DatabaseCreatedMessage(IAccountContext context, CosmosDatabase database) 9 | { 10 | Context = context; 11 | Database = database; 12 | } 13 | 14 | public IAccountContext Context { get; } 15 | public CosmosDatabase Database { get; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/Messages/DatabaseDeletedMessage.cs: -------------------------------------------------------------------------------- 1 | using CosmosDBStudio.Model; 2 | using CosmosDBStudio.Model.Services; 3 | 4 | namespace CosmosDBStudio.ViewModel.Messages 5 | { 6 | public class DatabaseDeletedMessage 7 | { 8 | public DatabaseDeletedMessage(IAccountContext context, CosmosDatabase database) 9 | { 10 | Context = context; 11 | Database = database; 12 | } 13 | 14 | public IAccountContext Context { get; } 15 | public CosmosDatabase Database { get; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/Messages/ExplorerSelectedContainerChangedMessage.cs: -------------------------------------------------------------------------------- 1 | using CosmosDBStudio.ViewModel.TreeNodes; 2 | 3 | namespace CosmosDBStudio.ViewModel.Messages 4 | { 5 | public class ExplorerSelectedContainerChangedMessage 6 | { 7 | public ExplorerSelectedContainerChangedMessage(ContainerNodeViewModel? container) 8 | { 9 | Container = container; 10 | } 11 | 12 | public ContainerNodeViewModel? Container { get; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/Messages/NewQuerySheetMessage.cs: -------------------------------------------------------------------------------- 1 | using CosmosDBStudio.Model.Services; 2 | 3 | namespace CosmosDBStudio.ViewModel.Messages 4 | { 5 | public class NewQuerySheetMessage 6 | { 7 | public NewQuerySheetMessage(IContainerContext context) 8 | { 9 | Context = context; 10 | } 11 | 12 | public IContainerContext Context { get; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/Messages/OpenScriptMessage.cs: -------------------------------------------------------------------------------- 1 | using CosmosDBStudio.Model.Services; 2 | 3 | namespace CosmosDBStudio.ViewModel.Messages 4 | { 5 | public class OpenScriptMessage 6 | { 7 | public OpenScriptMessage(IContainerContext context, TScript script) 8 | { 9 | Context = context; 10 | Script = script; 11 | } 12 | 13 | public IContainerContext Context { get; } 14 | public TScript Script { get; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/Messages/SetStatusBarMessage.cs: -------------------------------------------------------------------------------- 1 | namespace CosmosDBStudio.ViewModel.Messages 2 | { 3 | public class SetStatusBarMessage 4 | { 5 | public SetStatusBarMessage(string text) 6 | { 7 | Text = text; 8 | } 9 | 10 | public string Text { get; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/Services/IClipboardService.cs: -------------------------------------------------------------------------------- 1 | namespace CosmosDBStudio.ViewModel.Services 2 | { 3 | public interface IClipboardService 4 | { 5 | bool TryGetText(out string text); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/Services/IDialogService.cs: -------------------------------------------------------------------------------- 1 | using CosmosDBStudio.ViewModel.Dialogs; 2 | using Hamlet; 3 | 4 | namespace CosmosDBStudio.ViewModel.Services 5 | { 6 | public interface IDialogService 7 | { 8 | bool? ShowDialog(IDialogViewModel dialog); 9 | bool Confirm(string text); 10 | Option YesNoCancel(string text); 11 | void ShowError(string message); 12 | 13 | Option PickFileToSave( 14 | Option filter = default, 15 | Option filterIndex = default, 16 | Option fileName = default, 17 | Option initialDirectory = default); 18 | 19 | Option PickFileToOpen( 20 | Option filter = default, 21 | Option filterIndex = default, 22 | Option fileName = default, 23 | Option initialDirectory = default); 24 | 25 | Option TextPrompt(string prompt, Option initialText = default); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/Services/IUIDispatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace CosmosDBStudio.ViewModel.Services 5 | { 6 | public interface IUIDispatcher 7 | { 8 | void Invoke(Action action); 9 | Task InvokeAsync(Action action); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/TreeNodes/AccountFolderNodeViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using CosmosDBStudio.Model; 6 | using CosmosDBStudio.Model.Services; 7 | using CosmosDBStudio.ViewModel.Commands; 8 | 9 | namespace CosmosDBStudio.ViewModel.TreeNodes 10 | { 11 | public class AccountFolderNodeViewModel : NonLeafTreeNodeViewModel 12 | { 13 | private readonly CosmosAccountFolder _folder; 14 | private readonly AccountCommands _accountCommands; 15 | private readonly IAccountContextFactory _accountContextFactory; 16 | private readonly IAccountDirectory _accountDirectory; 17 | private readonly IViewModelFactory _viewModelFactory; 18 | 19 | public AccountFolderNodeViewModel( 20 | CosmosAccountFolder folder, 21 | AccountFolderNodeViewModel? parent, 22 | AccountCommands accountCommands, 23 | IAccountContextFactory accountContextFactory, 24 | IAccountDirectory accountDirectory, 25 | IViewModelFactory viewModelFactory) 26 | { 27 | _folder = folder; 28 | Parent = parent; 29 | _accountCommands = accountCommands; 30 | _accountContextFactory = accountContextFactory; 31 | _accountDirectory = accountDirectory; 32 | _viewModelFactory = viewModelFactory; 33 | 34 | Commands = new[] 35 | { 36 | new CommandViewModel("Add account", _accountCommands.AddCommand, this) 37 | }; 38 | } 39 | 40 | public override string Text => _folder.Name; 41 | 42 | public string Name => _folder.Name; 43 | public string FullPath => _folder.FullPath; 44 | 45 | public override NonLeafTreeNodeViewModel? Parent { get; } 46 | 47 | protected override Task> LoadChildrenAsync() 48 | { 49 | var childNodes = _accountDirectory.GetChildNodes(_folder.FullPath); 50 | var result = new List(); 51 | foreach (var node in childNodes) 52 | { 53 | var childVM = node switch 54 | { 55 | CosmosAccount account => 56 | (TreeNodeViewModel)_viewModelFactory.CreateAccountNode( 57 | account, 58 | _accountContextFactory.Create(account), 59 | this), 60 | CosmosAccountFolder folder => (TreeNodeViewModel)_viewModelFactory.CreateAccountFolderNode(folder, this), 61 | _ => throw new Exception("Invalid node type") 62 | }; 63 | result.Add(childVM); 64 | } 65 | return Task.FromResult>(result); 66 | } 67 | 68 | public override IEnumerable Commands { get; } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/TreeNodes/ContainerNodeViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using System.Windows.Input; 4 | using CosmosDBStudio.Model; 5 | using CosmosDBStudio.Model.Services; 6 | using CosmosDBStudio.ViewModel.Commands; 7 | using EssentialMVVM; 8 | 9 | namespace CosmosDBStudio.ViewModel.TreeNodes 10 | { 11 | public class ContainerNodeViewModel : NonLeafTreeNodeViewModel 12 | { 13 | private readonly ContainerCommands _containerCommands; 14 | private readonly IViewModelFactory _viewModelFactory; 15 | 16 | public DatabaseNodeViewModel Database { get; } 17 | public string Id { get; } 18 | 19 | public ContainerNodeViewModel( 20 | DatabaseNodeViewModel database, 21 | CosmosContainer container, 22 | IContainerContext context, 23 | ContainerCommands containerCommands, 24 | IViewModelFactory viewModelFactory) 25 | { 26 | Database = database; 27 | Id = container.Id; 28 | Context = context; 29 | _containerCommands = containerCommands; 30 | _viewModelFactory = viewModelFactory; 31 | Commands = new[] 32 | { 33 | new CommandViewModel("New query sheet", containerCommands.NewQuerySheetCommand, this, isDefault: true), 34 | CommandViewModel.Separator(), 35 | new CommandViewModel("Refresh", RefreshCommand), 36 | CommandViewModel.Separator(), 37 | new CommandViewModel("Create container", containerCommands.CreateCommand, Database), 38 | new CommandViewModel("Edit container", containerCommands.EditCommand, this), 39 | new CommandViewModel("Delete container", containerCommands.DeleteCommand, this), 40 | }; 41 | } 42 | 43 | private DelegateCommand? _newQuerySheetCommand; 44 | public ICommand NewQuerySheetCommand => _newQuerySheetCommand ??= _containerCommands.NewQuerySheetCommand.WithParameter(this); 45 | 46 | public override string Text => Id; 47 | 48 | public override IEnumerable Commands { get; } 49 | 50 | public override NonLeafTreeNodeViewModel? Parent => Database; 51 | 52 | public string Path => $"{Database.Account.Name}/{Database.Id}/{Id}"; 53 | 54 | public IContainerContext Context { get; } 55 | 56 | protected override Task> LoadChildrenAsync() 57 | { 58 | return Task.FromResult(GetChildren()); 59 | 60 | IEnumerable GetChildren() 61 | { 62 | yield return _viewModelFactory.CreateStoredProceduresFolderNode(Context, this); 63 | yield return _viewModelFactory.CreateUserDefinedFunctionsFolderNode(Context, this); 64 | yield return _viewModelFactory.CreateTriggersFolderNode(Context, this); 65 | } 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/TreeNodes/ScriptFolderNodeViewModel.cs: -------------------------------------------------------------------------------- 1 | using CosmosDBStudio.Model.Services; 2 | 3 | namespace CosmosDBStudio.ViewModel.TreeNodes 4 | { 5 | public abstract class ScriptFolderNodeViewModel : NonLeafTreeNodeViewModel 6 | { 7 | protected ScriptFolderNodeViewModel( 8 | string text, 9 | IContainerContext context, 10 | NonLeafTreeNodeViewModel parent, 11 | IViewModelFactory viewModelFactory) 12 | { 13 | Parent = parent; 14 | Text = text; 15 | Context = context; 16 | ViewModelFactory = viewModelFactory; 17 | } 18 | 19 | public override string Text { get; } 20 | public IContainerContext Context { get; } 21 | 22 | public override NonLeafTreeNodeViewModel? Parent { get; } 23 | protected IViewModelFactory ViewModelFactory { get; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/TreeNodes/ScriptNodeViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using System.Windows.Input; 4 | using CosmosDBStudio.Model; 5 | using CosmosDBStudio.Model.Services; 6 | using CosmosDBStudio.ViewModel.Commands; 7 | using EssentialMVVM; 8 | 9 | namespace CosmosDBStudio.ViewModel.TreeNodes 10 | { 11 | public abstract class ScriptNodeViewModel : TreeNodeViewModel 12 | { 13 | } 14 | 15 | public abstract class ScriptNodeViewModel : ScriptNodeViewModel 16 | where TScript : ICosmosScript, new() 17 | { 18 | private readonly ScriptCommands _commands; 19 | 20 | protected ScriptNodeViewModel( 21 | TScript script, 22 | IContainerContext context, 23 | NonLeafTreeNodeViewModel parent, 24 | ScriptCommands commands, 25 | IMessenger messenger) 26 | { 27 | Parent = parent; 28 | Script = script; 29 | Context = context; 30 | _commands = commands; 31 | Messenger = messenger; 32 | Commands = new[] 33 | { 34 | new CommandViewModel($"Open {Description}", commands.OpenCommand, this, isDefault: true), 35 | CommandViewModel.Separator(), 36 | new CommandViewModel($"Create new {Description}", commands.CreateCommand, parent), 37 | new CommandViewModel($"Delete {Description}", commands.DeleteCommand, this), 38 | }; 39 | } 40 | 41 | public TScript Script { get; } 42 | public IContainerContext Context { get; } 43 | public IMessenger Messenger { get; } 44 | 45 | public override string Text => Script.Id; 46 | public override NonLeafTreeNodeViewModel? Parent { get; } 47 | 48 | public override IEnumerable Commands { get; } 49 | 50 | public abstract string Description { get; } 51 | 52 | private DelegateCommand? _openCommand; 53 | public ICommand OpenCommand => _openCommand ??= _commands.OpenCommand.WithParameter(this); 54 | 55 | public abstract Task DeleteAsync(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/TreeNodes/StoredProcedureNodeViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using CosmosDBStudio.Model; 3 | using CosmosDBStudio.Model.Services; 4 | using CosmosDBStudio.ViewModel.Commands; 5 | 6 | namespace CosmosDBStudio.ViewModel.TreeNodes 7 | { 8 | public class StoredProcedureNodeViewModel : ScriptNodeViewModel 9 | { 10 | public StoredProcedureNodeViewModel( 11 | IContainerContext context, 12 | CosmosStoredProcedure storedProcedure, 13 | NonLeafTreeNodeViewModel parent, 14 | ScriptCommands commands, 15 | IMessenger messenger) 16 | : base(storedProcedure, context, parent, commands, messenger) 17 | { 18 | } 19 | 20 | public override string Description => "stored procedure"; 21 | 22 | public override Task DeleteAsync() => Context.Scripts.DeleteStoredProcedureAsync(Script, default); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/TreeNodes/StoredProceduresFolderNodeViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using CosmosDBStudio.Model; 5 | using CosmosDBStudio.Model.Services; 6 | using CosmosDBStudio.ViewModel.Commands; 7 | 8 | namespace CosmosDBStudio.ViewModel.TreeNodes 9 | { 10 | public class StoredProceduresFolderNodeViewModel : ScriptFolderNodeViewModel 11 | { 12 | public StoredProceduresFolderNodeViewModel( 13 | IContainerContext context, 14 | NonLeafTreeNodeViewModel parent, 15 | ScriptCommands commands, 16 | IViewModelFactory viewModelFactory) 17 | : base("Stored procedures", context, parent, viewModelFactory) 18 | { 19 | Commands = new[] 20 | { 21 | new CommandViewModel($"New stored procedure", commands.CreateCommand, this), 22 | CommandViewModel.Separator(), 23 | new CommandViewModel("Refresh", RefreshCommand) 24 | }; 25 | } 26 | 27 | public override IEnumerable Commands { get; } 28 | 29 | protected async override Task> LoadChildrenAsync() 30 | { 31 | var storedProcedures = await Context.Scripts.GetStoredProceduresAsync(default); 32 | return storedProcedures.Select(sp => ViewModelFactory.CreateStoredProcedureNode(sp, Context, this)); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/TreeNodes/TreeNodeViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Windows.Input; 4 | using EssentialMVVM; 5 | 6 | namespace CosmosDBStudio.ViewModel.TreeNodes 7 | { 8 | public abstract class TreeNodeViewModel : BindableBase 9 | { 10 | public abstract string Text { get; } 11 | 12 | public abstract NonLeafTreeNodeViewModel? Parent { get; } 13 | 14 | public virtual IEnumerable Commands => Enumerable.Empty(); 15 | 16 | private bool _isExpanded; 17 | public bool IsExpanded 18 | { 19 | get => _isExpanded; 20 | set => Set(ref _isExpanded, value).AndExecute(OnIsExpandedChanged); 21 | } 22 | 23 | protected virtual void OnIsExpandedChanged() 24 | { 25 | } 26 | 27 | private string? _error; 28 | public string? Error 29 | { 30 | get => _error; 31 | set => Set(ref _error, value).AndNotifyPropertyChanged(nameof(HasError)); 32 | } 33 | 34 | public bool HasError => !string.IsNullOrEmpty(Error); 35 | 36 | public ICommand? DefaultCommand => GetDefaultCommand()?.Command; 37 | public object? DefaultCommandParameter => GetDefaultCommand()?.CommandParameter; 38 | 39 | private CommandViewModel? GetDefaultCommand() => Commands?.FirstOrDefault(c => c.IsDefault); 40 | } 41 | } -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/TreeNodes/TriggerNodeViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using CosmosDBStudio.Model; 3 | using CosmosDBStudio.Model.Services; 4 | using CosmosDBStudio.ViewModel.Commands; 5 | 6 | namespace CosmosDBStudio.ViewModel.TreeNodes 7 | { 8 | public class TriggerNodeViewModel : ScriptNodeViewModel 9 | { 10 | public TriggerNodeViewModel( 11 | CosmosTrigger trigger, 12 | IContainerContext context, 13 | NonLeafTreeNodeViewModel parent, 14 | ScriptCommands commands, 15 | IMessenger messenger) 16 | : base(trigger, context, parent, commands, messenger) 17 | { 18 | } 19 | 20 | public override string Description => "trigger"; 21 | 22 | public override Task DeleteAsync() => Context.Scripts.DeleteTriggerAsync(Script, default); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/TreeNodes/TriggersFolderNodeViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using CosmosDBStudio.Model; 5 | using CosmosDBStudio.Model.Services; 6 | using CosmosDBStudio.ViewModel.Commands; 7 | 8 | namespace CosmosDBStudio.ViewModel.TreeNodes 9 | { 10 | public class TriggersFolderNodeViewModel : ScriptFolderNodeViewModel 11 | { 12 | public TriggersFolderNodeViewModel( 13 | IContainerContext containerContext, 14 | NonLeafTreeNodeViewModel parent, 15 | ScriptCommands commands, 16 | IViewModelFactory viewModelFactory) 17 | : base("Triggers", containerContext, parent, viewModelFactory) 18 | { 19 | Commands = new[] 20 | { 21 | new CommandViewModel($"New trigger", commands.CreateCommand, this), 22 | CommandViewModel.Separator(), 23 | new CommandViewModel("Refresh", RefreshCommand) 24 | }; 25 | } 26 | 27 | public override IEnumerable Commands { get; } 28 | 29 | protected async override Task> LoadChildrenAsync() 30 | { 31 | var triggers = await Context.Scripts.GetTriggersAsync(default); 32 | return triggers.Select(t => ViewModelFactory.CreateTriggerNode(t, Context, this)); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/TreeNodes/UserDefinedFunctionNodeViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using CosmosDBStudio.Model; 3 | using CosmosDBStudio.Model.Services; 4 | using CosmosDBStudio.ViewModel.Commands; 5 | 6 | namespace CosmosDBStudio.ViewModel.TreeNodes 7 | { 8 | public class UserDefinedFunctionNodeViewModel : ScriptNodeViewModel 9 | { 10 | public UserDefinedFunctionNodeViewModel( 11 | CosmosUserDefinedFunction udf, 12 | IContainerContext context, 13 | NonLeafTreeNodeViewModel parent, 14 | ScriptCommands commands, 15 | IMessenger messenger) 16 | : base(udf, context, parent, commands, messenger) 17 | { 18 | } 19 | 20 | public override string Description => "user-defined function"; 21 | 22 | public override Task DeleteAsync() => Context.Scripts.DeleteUserDefinedFunctionAsync(Script, default); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/TreeNodes/UserDefinedFunctionsFolderNodeViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using CosmosDBStudio.Model; 5 | using CosmosDBStudio.Model.Services; 6 | using CosmosDBStudio.ViewModel.Commands; 7 | 8 | namespace CosmosDBStudio.ViewModel.TreeNodes 9 | { 10 | public class UserDefinedFunctionsFolderNodeViewModel : ScriptFolderNodeViewModel 11 | { 12 | public UserDefinedFunctionsFolderNodeViewModel( 13 | IContainerContext containerContext, 14 | NonLeafTreeNodeViewModel parent, 15 | ScriptCommands commands, 16 | IViewModelFactory viewModelFactory) 17 | : base("User-defined functions", containerContext, parent, viewModelFactory) 18 | { 19 | Commands = new[] 20 | { 21 | new CommandViewModel($"New user-defined function", commands.CreateCommand, this), 22 | CommandViewModel.Separator(), 23 | new CommandViewModel("Refresh", RefreshCommand), 24 | }; 25 | } 26 | 27 | public override IEnumerable Commands { get; } 28 | 29 | protected async override Task> LoadChildrenAsync() 30 | { 31 | var functions = await Context.Scripts.GetUserDefinedFunctionsAsync(default); 32 | return functions.Select(udf => ViewModelFactory.CreateUserDefinedFunctionNode(udf, Context, this)); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/CosmosDBStudio.ViewModel/ViewModelFactoryProxy.cs: -------------------------------------------------------------------------------- 1 | using Castle.DynamicProxy; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using System; 4 | using System.Collections.Concurrent; 5 | using System.Linq; 6 | using System.Reflection; 7 | 8 | namespace CosmosDBStudio.ViewModel 9 | { 10 | public static class ViewModelFactoryProxy 11 | { 12 | public static IViewModelFactory Create(IServiceProvider sp) 13 | { 14 | var generator = new ProxyGenerator(); 15 | return generator.CreateInterfaceProxyWithoutTarget( 16 | new ViewModelFactoryInterceptor(sp)); 17 | } 18 | 19 | private class ViewModelFactoryInterceptor : IInterceptor 20 | { 21 | private readonly IServiceProvider _serviceProvider; 22 | private readonly ConcurrentDictionary _factories; 23 | 24 | public ViewModelFactoryInterceptor(IServiceProvider serviceProvider) 25 | { 26 | _serviceProvider = serviceProvider; 27 | _factories = new ConcurrentDictionary(); 28 | } 29 | 30 | public void Intercept(IInvocation invocation) 31 | { 32 | var factory = _factories.GetOrAdd(invocation.Method, CreateFactory); 33 | invocation.ReturnValue = factory(_serviceProvider, invocation.Arguments); 34 | } 35 | 36 | private ObjectFactory CreateFactory(MethodInfo method) 37 | { 38 | return ActivatorUtilities.CreateFactory( 39 | method.ReturnType, 40 | method.GetParameters().Select(p => p.ParameterType).ToArray()); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/App.xaml: -------------------------------------------------------------------------------- 1 |  4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using CosmosDBStudio.SyntaxHighlighting; 2 | using CosmosDBStudio.ViewModel; 3 | using System.Windows; 4 | using CosmosDBStudio.Model.Services; 5 | using CosmosDBStudio.Services; 6 | 7 | namespace CosmosDBStudio 8 | { 9 | public partial class App : IApplication 10 | { 11 | private readonly MainWindowViewModel _mainWindowViewModel; 12 | 13 | public App(MainWindowViewModel mainWindowViewModel) 14 | { 15 | CosmosSyntax.Init(); 16 | InitializeComponent(); 17 | _mainWindowViewModel = mainWindowViewModel; 18 | } 19 | 20 | public static new App Current => (App)Application.Current; 21 | 22 | public bool IsShuttingDown { get; private set; } 23 | 24 | protected override void OnStartup(StartupEventArgs e) 25 | { 26 | base.OnStartup(e); 27 | MainWindow = new MainWindow(); 28 | MainWindow.DataContext = _mainWindowViewModel; 29 | MainWindow.Show(); 30 | } 31 | 32 | public void Quit() 33 | { 34 | IsShuttingDown = true; 35 | Shutdown(); 36 | } 37 | 38 | public ApplicationVersionInfo GetVersionInfo() 39 | { 40 | var package = global::Windows.ApplicationModel.Package.Current; 41 | var v = package.Id.Version; 42 | return new ApplicationVersionInfo(package.DisplayName, $"{v.Major}.{v.Minor}.{v.Build}.{v.Revision}", package.PublisherDisplayName); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/Behaviors/AvalonTextEditorBehavior.cs: -------------------------------------------------------------------------------- 1 | using ICSharpCode.AvalonEdit; 2 | using ICSharpCode.AvalonEdit.Search; 3 | using Microsoft.Xaml.Behaviors; 4 | using System.Windows; 5 | 6 | namespace CosmosDBStudio.Behaviors 7 | { 8 | public class AvalonTextEditorBehavior : Behavior 9 | { 10 | protected override void OnAttached() 11 | { 12 | base.OnAttached(); 13 | OnUseSearchChanged(); 14 | } 15 | 16 | protected override void OnDetaching() 17 | { 18 | base.OnDetaching(); 19 | } 20 | 21 | public bool UseSearch 22 | { 23 | get { return (bool)GetValue(UseSearchProperty); } 24 | set { SetValue(UseSearchProperty, value); } 25 | } 26 | 27 | public static readonly DependencyProperty UseSearchProperty = 28 | DependencyProperty.Register( 29 | "UseSearch", 30 | typeof(bool), 31 | typeof(AvalonTextEditorBehavior), 32 | new PropertyMetadata(false, OnUseSearchPropertyChanged)); 33 | 34 | private static void OnUseSearchPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 35 | { 36 | if (d is AvalonTextEditorBehavior behavior) 37 | { 38 | behavior.OnUseSearchChanged(); 39 | } 40 | } 41 | 42 | private SearchPanel? _searchPanel; 43 | private void OnUseSearchChanged() 44 | { 45 | if (AssociatedObject is null) 46 | return; 47 | 48 | if (UseSearch) 49 | { 50 | _searchPanel ??= SearchPanel.Install(AssociatedObject.TextArea); 51 | } 52 | else if (_searchPanel != null) 53 | { 54 | _searchPanel.Uninstall(); 55 | _searchPanel = null; 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/Behaviors/FocusOnLoadBehavior.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xaml.Behaviors; 2 | using System.Windows; 3 | 4 | namespace CosmosDBStudio.Behaviors 5 | { 6 | public class FocusOnLoadBehavior : Behavior 7 | { 8 | protected override void OnAttached() 9 | { 10 | base.OnAttached(); 11 | AssociatedObject.Loaded += AssociatedObject_Loaded; 12 | } 13 | 14 | protected override void OnDetaching() 15 | { 16 | AssociatedObject.Loaded -= AssociatedObject_Loaded; 17 | base.OnDetaching(); 18 | } 19 | 20 | private void AssociatedObject_Loaded(object sender, System.Windows.RoutedEventArgs e) 21 | { 22 | ((FrameworkElement)sender).Focus(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/Behaviors/OpenContextMenuBehavior.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xaml.Behaviors; 2 | using System.Windows.Controls; 3 | using System.Windows.Controls.Primitives; 4 | 5 | namespace CosmosDBStudio.Behaviors 6 | { 7 | public class OpenContextMenuBehavior : Behavior 8 | { 9 | protected override void OnAttached() 10 | { 11 | base.OnAttached(); 12 | AssociatedObject.Click += AssociatedObject_Click; 13 | } 14 | 15 | protected override void OnDetaching() 16 | { 17 | AssociatedObject.Click -= AssociatedObject_Click; 18 | base.OnDetaching(); 19 | } 20 | 21 | private void AssociatedObject_Click(object sender, System.Windows.RoutedEventArgs e) 22 | { 23 | if (AssociatedObject.ContextMenu is ContextMenu menu) 24 | { 25 | menu.PlacementTarget = AssociatedObject; 26 | menu.IsOpen = true; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/Behaviors/TreeViewSelectedItemBindingBehavior.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xaml.Behaviors; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | 5 | namespace CosmosDBStudio.Behaviors 6 | { 7 | public class TreeViewSelectedItemBindingBehavior : Behavior 8 | { 9 | protected override void OnAttached() 10 | { 11 | base.OnAttached(); 12 | AssociatedObject.SelectedItemChanged += AssociatedItemSelectedItemChanged; 13 | } 14 | 15 | protected override void OnDetaching() 16 | { 17 | AssociatedObject.SelectedItemChanged -= AssociatedItemSelectedItemChanged; 18 | base.OnDetaching(); 19 | } 20 | 21 | /// 22 | /// Note: one-way-to-source only 23 | /// 24 | public object SelectedItem 25 | { 26 | get { return (object)GetValue(SelectedItemProperty); } 27 | set { SetValue(SelectedItemProperty, value); } 28 | } 29 | 30 | public static readonly DependencyProperty SelectedItemProperty = 31 | DependencyProperty.Register( 32 | "SelectedItem", 33 | typeof(object), 34 | typeof(TreeViewSelectedItemBindingBehavior), 35 | new PropertyMetadata(null)); 36 | 37 | private void AssociatedItemSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs args) 38 | { 39 | if (SelectedItem != args.NewValue) 40 | this.SetCurrentValue(SelectedItemProperty, args.NewValue); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/Converters/EnumToInt32Converter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | 6 | namespace CosmosDBStudio.Converters 7 | { 8 | public class EnumToInt32Converter : IValueConverter 9 | { 10 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 11 | { 12 | if (value is null) 13 | return DependencyProperty.UnsetValue; 14 | 15 | var type = value.GetType(); 16 | if (!type.IsEnum) 17 | return DependencyProperty.UnsetValue; 18 | 19 | return (int)value; 20 | } 21 | 22 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 23 | { 24 | if (!targetType.IsEnum) 25 | return DependencyProperty.UnsetValue; 26 | 27 | if (value is null) 28 | return DependencyProperty.UnsetValue; 29 | 30 | return Enum.ToObject(targetType, value); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/Converters/NotConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | namespace CosmosDBStudio.Converters 6 | { 7 | public class NotConverter : IValueConverter 8 | { 9 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 10 | { 11 | if (value is bool b) 12 | return !b; 13 | 14 | return Binding.DoNothing; 15 | } 16 | 17 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 18 | { 19 | return Convert(value, targetType, parameter, culture); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/Converters/SuppressAccessKeyConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | namespace CosmosDBStudio.Converters 6 | { 7 | public class SuppressAccessKeyConverter : IValueConverter 8 | { 9 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 10 | { 11 | if (value is string s) 12 | { 13 | int underscore = s.IndexOf('_'); 14 | if (underscore < 0) 15 | return s; 16 | return s.Insert(underscore, "_"); 17 | } 18 | 19 | return value; 20 | } 21 | 22 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 23 | { 24 | throw new NotSupportedException(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/CosmosDBStudio.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net6.0-windows10.0.18362.0 6 | true 7 | AnyCPU;x64;x86 8 | 9 | 10 | 11 | Images\app.ico 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/DataTemplates.xaml: -------------------------------------------------------------------------------- 1 |  7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/Extensions/CommandExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Input; 2 | 3 | namespace CosmosDBStudio.Extensions 4 | { 5 | public static class CommandExtensions 6 | { 7 | public static bool TryExecute(this ICommand? command, object? parameter) 8 | { 9 | if (command is null) 10 | return false; 11 | 12 | if (command.CanExecute(parameter)) 13 | { 14 | command.Execute(parameter); 15 | return true; 16 | } 17 | 18 | return false; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/Extensions/UIElementExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Media; 3 | 4 | namespace CosmosDBStudio.Extensions 5 | { 6 | public static class UIElementExtensions 7 | { 8 | public static TAncestor? GetAncestorOrSelf(this UIElement element) 9 | where TAncestor : UIElement 10 | { 11 | UIElement? uiElement = element; 12 | while ((uiElement != null) && !(uiElement is TAncestor)) 13 | uiElement = VisualTreeHelper.GetParent(uiElement) as UIElement; 14 | 15 | return uiElement as TAncestor; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/Images/account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/src/CosmosDBStudio/Images/account.png -------------------------------------------------------------------------------- /src/CosmosDBStudio/Images/app.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/src/CosmosDBStudio/Images/app.ico -------------------------------------------------------------------------------- /src/CosmosDBStudio/Images/container.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/src/CosmosDBStudio/Images/container.png -------------------------------------------------------------------------------- /src/CosmosDBStudio/Images/database.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/src/CosmosDBStudio/Images/database.png -------------------------------------------------------------------------------- /src/CosmosDBStudio/Images/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/src/CosmosDBStudio/Images/folder.png -------------------------------------------------------------------------------- /src/CosmosDBStudio/Images/hi-res/account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/src/CosmosDBStudio/Images/hi-res/account.png -------------------------------------------------------------------------------- /src/CosmosDBStudio/Images/hi-res/container.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/src/CosmosDBStudio/Images/hi-res/container.png -------------------------------------------------------------------------------- /src/CosmosDBStudio/Images/hi-res/database.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/src/CosmosDBStudio/Images/hi-res/database.png -------------------------------------------------------------------------------- /src/CosmosDBStudio/Images/hi-res/stored-procedure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/src/CosmosDBStudio/Images/hi-res/stored-procedure.png -------------------------------------------------------------------------------- /src/CosmosDBStudio/Images/hi-res/trigger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/src/CosmosDBStudio/Images/hi-res/trigger.png -------------------------------------------------------------------------------- /src/CosmosDBStudio/Images/hi-res/user-defined-function.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/src/CosmosDBStudio/Images/hi-res/user-defined-function.png -------------------------------------------------------------------------------- /src/CosmosDBStudio/Images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/src/CosmosDBStudio/Images/logo.png -------------------------------------------------------------------------------- /src/CosmosDBStudio/Images/stored-procedure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/src/CosmosDBStudio/Images/stored-procedure.png -------------------------------------------------------------------------------- /src/CosmosDBStudio/Images/trigger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/src/CosmosDBStudio/Images/trigger.png -------------------------------------------------------------------------------- /src/CosmosDBStudio/Images/user-defined-function.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/src/CosmosDBStudio/Images/user-defined-function.png -------------------------------------------------------------------------------- /src/CosmosDBStudio/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using CosmosDBStudio.ViewModel; 2 | using System.ComponentModel; 3 | 4 | namespace CosmosDBStudio 5 | { 6 | public partial class MainWindow 7 | { 8 | public MainWindow() 9 | { 10 | InitializeComponent(); 11 | } 12 | 13 | protected override void OnClosing(CancelEventArgs e) 14 | { 15 | if (App.Current.IsShuttingDown) 16 | return; 17 | 18 | e.Cancel = true; 19 | if (DataContext is MainWindowViewModel vm) 20 | { 21 | vm.QuitCommand.Execute(null); 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/Markup/BindingProxy.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace CosmosDBStudio.Markup 4 | { 5 | public class BindingProxy : Freezable 6 | { 7 | #region Overrides of Freezable 8 | 9 | protected override Freezable CreateInstanceCore() 10 | { 11 | return new BindingProxy(); 12 | } 13 | 14 | #endregion 15 | 16 | public object Data 17 | { 18 | get { return (object)GetValue(DataProperty); } 19 | set { SetValue(DataProperty, value); } 20 | } 21 | 22 | public static readonly DependencyProperty DataProperty = 23 | DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/Markup/EnumValuesExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using System.Windows.Markup; 4 | 5 | namespace CosmosDBStudio.Markup 6 | { 7 | public class EnumValuesExtension : MarkupExtension 8 | { 9 | public EnumValuesExtension() 10 | { 11 | } 12 | 13 | public EnumValuesExtension(Type enumType) 14 | { 15 | EnumType = enumType; 16 | } 17 | 18 | public Type? EnumType { get; set; } 19 | 20 | public override object ProvideValue(IServiceProvider serviceProvider) 21 | { 22 | if (EnumType is null || !EnumType.IsEnum) 23 | return DependencyProperty.UnsetValue; 24 | 25 | return Enum.GetValues(EnumType); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/Markup/SwitchExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | namespace CosmosDBStudio.Markup 6 | { 7 | public class SwitchExtension : Binding 8 | { 9 | public SwitchExtension() 10 | { 11 | Converter = new SwitchConverter(this); 12 | } 13 | 14 | public SwitchExtension(string path) 15 | : base(path) 16 | { 17 | Converter = new SwitchConverter(this); 18 | } 19 | 20 | public object? TrueValue { get; set; } 21 | public object? FalseValue { get; set; } 22 | 23 | private class SwitchConverter : IValueConverter 24 | { 25 | private readonly SwitchExtension _switch; 26 | 27 | public SwitchConverter(SwitchExtension @switch) 28 | { 29 | _switch = @switch; 30 | } 31 | 32 | public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) 33 | { 34 | if (value is true) 35 | return _switch.TrueValue; 36 | if (value is false) 37 | return _switch.FalseValue; 38 | return _switch.FallbackValue; 39 | } 40 | 41 | public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) 42 | { 43 | throw new NotSupportedException(); 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/Program.cs: -------------------------------------------------------------------------------- 1 | using CosmosDBStudio.Extensions; 2 | using CosmosDBStudio.Model; 3 | using CosmosDBStudio.Services; 4 | using CosmosDBStudio.Services.Implementation; 5 | using CosmosDBStudio.ViewModel; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using System; 8 | using CosmosDBStudio.Model.Services; 9 | using CosmosDBStudio.Model.Services.Implementation; 10 | using CosmosDBStudio.Util.Extensions; 11 | using CosmosDBStudio.ViewModel.Commands; 12 | using CosmosDBStudio.ViewModel.Services; 13 | using CosmosDBStudio.ViewModel.Services.Implementation; 14 | 15 | namespace CosmosDBStudio 16 | { 17 | static class Program 18 | { 19 | [STAThread] 20 | public static void Main(string[] args) 21 | { 22 | var serviceProvider = CreateServiceProvider(); 23 | var app = serviceProvider.GetRequiredService(); 24 | app.Run(); 25 | } 26 | 27 | private static IServiceProvider CreateServiceProvider() 28 | { 29 | var services = new ServiceCollection(); 30 | ConfigureServices(services); 31 | return services.BuildServiceProvider(); 32 | } 33 | 34 | private static void ConfigureServices(IServiceCollection services) 35 | { 36 | services.AddSingleton(); 37 | services.AddSingleton(); 38 | services.AddSingleton(); 39 | services.AddSingleton(); 40 | 41 | services.AddSingleton(ViewModelFactoryProxy.Create); 42 | services.AddSingleton(); 43 | services.AddSingleton(); 44 | services.AddSingleton(sp => sp.GetRequiredService().Dispatcher); 45 | services.AddSingleton(); 46 | services.AddSingleton(); 47 | 48 | services.AddLazyResolution(); 49 | 50 | services.AddSingleton(); 51 | services.AddSingleton(); 52 | services.AddSingleton(); 53 | services.AddSingleton>(); 54 | services.AddSingleton>(); 55 | services.AddSingleton>(); 56 | 57 | services.AddSingleton(); 58 | 59 | services.AddSingleton(); 60 | services.AddSingleton(sp => sp.GetRequiredService()); 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /src/CosmosDBStudio/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Resources; 2 | using System.Windows; 3 | 4 | [assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.MainAssembly)] 5 | 6 | 7 | [assembly: ThemeInfo( 8 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 9 | //(used if a resource is not found in the page, 10 | // or application resource dictionaries) 11 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 12 | //(used if a resource is not found in the page, 13 | // app, or any theme specific resource dictionaries) 14 | )] 15 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace CosmosDBStudio.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/Resources.xaml: -------------------------------------------------------------------------------- 1 |  4 | 5 | 6 | 7 | True 8 | False 9 | 10 | Cascadia Code, Consolas, Courier New 11 | pack://application:,,,/Resources/#Font Awesome 5 Free Solid 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/Resources/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/CosmosDBStudio/c91b22e7bcff87406ad76f6c5f05b61202905e66/src/CosmosDBStudio/Resources/fa-solid-900.ttf -------------------------------------------------------------------------------- /src/CosmosDBStudio/Services/Implementation/ClipboardService.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using CosmosDBStudio.ViewModel.Services; 3 | 4 | namespace CosmosDBStudio.Services.Implementation 5 | { 6 | public class ClipboardService : IClipboardService 7 | { 8 | public bool TryGetText(out string text) 9 | { 10 | if (Clipboard.ContainsText()) 11 | { 12 | text = Clipboard.GetText(); 13 | return true; 14 | } 15 | 16 | text = string.Empty; 17 | return false; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/Services/Implementation/UIDispatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using System.Windows.Threading; 4 | using CosmosDBStudio.ViewModel.Services; 5 | 6 | namespace CosmosDBStudio.Services.Implementation 7 | { 8 | public class UIDispatcher : IUIDispatcher 9 | { 10 | private readonly Lazy _dispatcher; 11 | 12 | public UIDispatcher(Lazy dispatcher) 13 | { 14 | _dispatcher = dispatcher; 15 | } 16 | 17 | public void Invoke(Action action) 18 | { 19 | _dispatcher.Value.Invoke(action); 20 | } 21 | 22 | public Task InvokeAsync(Action action) 23 | { 24 | var operation = _dispatcher.Value.InvokeAsync(action); 25 | return operation.Task; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/SyntaxHighlighting/CosmosSyntax.cs: -------------------------------------------------------------------------------- 1 | using ICSharpCode.AvalonEdit.Highlighting; 2 | using ICSharpCode.AvalonEdit.Highlighting.Xshd; 3 | using System; 4 | using System.Text.RegularExpressions; 5 | using System.Xml; 6 | 7 | namespace CosmosDBStudio.SyntaxHighlighting 8 | { 9 | internal static class CosmosSyntax 10 | { 11 | public static void Init() 12 | { 13 | var assembly = typeof(CosmosSyntax).Assembly; 14 | var regex = new Regex(@"CosmosDBStudio\.SyntaxHighlighting\.(?[a-zA-Z0-9-_]+).xshd"); 15 | var resourceNames = assembly.GetManifestResourceNames(); 16 | foreach (var resourceName in resourceNames) 17 | { 18 | var match = regex.Match(resourceName); 19 | if (!match.Success) 20 | continue; 21 | 22 | var syntaxName = match.Groups["syntaxName"].Value; 23 | using var stream = assembly.GetManifestResourceStream(resourceName)!; 24 | using var reader = XmlReader.Create(stream); 25 | var definition = HighlightingLoader.Load(reader, HighlightingManager.Instance); 26 | HighlightingManager.Instance.RegisterHighlighting(syntaxName, Array.Empty(), definition); 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/SyntaxHighlighting/JSON.xshd: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | " 19 | " 20 | 21 | 22 | ' 23 | ' 24 | 25 | 26 | : 27 | 28 | 29 | , 30 | 31 | 32 | 33 | 34 | 35 | 36 | , 37 | 38 | 39 | 40 | 41 | 42 | true 43 | false 44 | 45 | 46 | null 47 | 48 | 49 | " 50 | " 51 | 52 | 53 | ' 54 | ' 55 | 56 | 57 | \{ 58 | \} 59 | 60 | 61 | \[ 62 | \] 63 | 64 | 65 | \b0[xX][0-9a-fA-F]+|(\b\d+(\.[0-9]+)?|\.[0-9]+)([eE][+-]?[0-9]+)? 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/Themes/Generic.xaml: -------------------------------------------------------------------------------- 1 |  3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/Themes/RadioChoice.generic.xaml: -------------------------------------------------------------------------------- 1 |  4 | 5 | 16 | 17 | 29 | 30 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/Themes/TreeNodeControl.generic.xaml: -------------------------------------------------------------------------------- 1 |  4 | 28 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/View/AboutView.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | View on Github 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/View/AboutView.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Windows; 5 | using System.Windows.Controls; 6 | using System.Windows.Data; 7 | using System.Windows.Documents; 8 | using System.Windows.Input; 9 | using System.Windows.Media; 10 | using System.Windows.Media.Imaging; 11 | using System.Windows.Navigation; 12 | using System.Windows.Shapes; 13 | 14 | namespace CosmosDBStudio.View 15 | { 16 | /// 17 | /// Interaction logic for AboutView.xaml 18 | /// 19 | public partial class AboutView : UserControl 20 | { 21 | public AboutView() 22 | { 23 | InitializeComponent(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/View/AccountEditorView.xaml: -------------------------------------------------------------------------------- 1 |  12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 52 | 53 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/View/AccountEditorView.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace CosmosDBStudio.View 4 | { 5 | /// 6 | /// Interaction logic for AccountEditorView.xaml 7 | /// 8 | public partial class AccountEditorView : UserControl 9 | { 10 | public AccountEditorView() 11 | { 12 | InitializeComponent(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/View/AccountsView.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace CosmosDBStudio.View 4 | { 5 | public partial class AccountsView : UserControl 6 | { 7 | public AccountsView() 8 | { 9 | InitializeComponent(); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/View/ContainerEditorView.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace CosmosDBStudio.View 4 | { 5 | /// 6 | /// Interaction logic for ContainerEditorView.xaml 7 | /// 8 | public partial class ContainerEditorView : UserControl 9 | { 10 | public ContainerEditorView() 11 | { 12 | InitializeComponent(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/View/ContainerPickerView.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace CosmosDBStudio.View 4 | { 5 | /// 6 | /// Interaction logic for ContainerPickerView.xaml 7 | /// 8 | public partial class ContainerPickerView : UserControl 9 | { 10 | public ContainerPickerView() 11 | { 12 | InitializeComponent(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/View/Controls/AccountExplorerTreeView.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls; 3 | 4 | namespace CosmosDBStudio.View.Controls 5 | { 6 | public class AccountExplorerTreeView : TreeView 7 | { 8 | protected override DependencyObject GetContainerForItemOverride() 9 | { 10 | return new AccountExplorerTreeViewItem(); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/View/Controls/AccountExplorerTreeViewItem.cs: -------------------------------------------------------------------------------- 1 | using CosmosDBStudio.Extensions; 2 | using CosmosDBStudio.ViewModel; 3 | using System.Windows; 4 | using System.Windows.Controls; 5 | using System.Windows.Input; 6 | using CosmosDBStudio.ViewModel.TreeNodes; 7 | 8 | namespace CosmosDBStudio.View.Controls 9 | { 10 | public class AccountExplorerTreeViewItem : TreeViewItem 11 | { 12 | protected override DependencyObject GetContainerForItemOverride() 13 | { 14 | return new AccountExplorerTreeViewItem(); 15 | } 16 | 17 | protected override void OnMouseDoubleClick(MouseButtonEventArgs e) 18 | { 19 | var clickedTreeViewItem = (e.OriginalSource as UIElement)?.GetAncestorOrSelf(); 20 | if (clickedTreeViewItem != this) 21 | return; 22 | 23 | e.Handled = TryExecuteDefaultCommand(); 24 | base.OnMouseDoubleClick(e); 25 | } 26 | 27 | protected override void OnKeyDown(KeyEventArgs e) 28 | { 29 | e.Handled = e.Key == Key.Enter && TryExecuteDefaultCommand(); 30 | base.OnKeyDown(e); 31 | } 32 | 33 | private bool TryExecuteDefaultCommand() 34 | { 35 | if (DataContext is TreeNodeViewModel vm) 36 | { 37 | return vm.DefaultCommand.TryExecute(vm.DefaultCommandParameter); 38 | } 39 | 40 | return false; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/View/Controls/CosmosTabControl.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls; 3 | 4 | namespace CosmosDBStudio.View.Controls 5 | { 6 | public class CosmosTabControl : TabControl 7 | { 8 | protected override bool IsItemItsOwnContainerOverride(object item) 9 | { 10 | return item is CosmosTabItem; 11 | } 12 | 13 | protected override DependencyObject GetContainerForItemOverride() 14 | { 15 | return new CosmosTabItem(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/View/Controls/RadioChoice.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | 5 | namespace CosmosDBStudio.View.Controls 6 | { 7 | public class RadioChoice : ListBox 8 | { 9 | static RadioChoice() 10 | { 11 | DefaultStyleKeyProperty.OverrideMetadata( 12 | typeof(RadioChoice), 13 | new FrameworkPropertyMetadata(typeof(RadioChoice))); 14 | } 15 | 16 | private readonly string _groupName = Guid.NewGuid().ToString(); 17 | 18 | public Orientation Orientation 19 | { 20 | get { return (Orientation)GetValue(OrientationProperty); } 21 | set { SetValue(OrientationProperty, value); } 22 | } 23 | 24 | public static readonly DependencyProperty OrientationProperty = 25 | DependencyProperty.Register("Orientation", typeof(Orientation), typeof(RadioChoice), new PropertyMetadata(Orientation.Vertical)); 26 | 27 | protected override DependencyObject GetContainerForItemOverride() 28 | { 29 | return new RadioChoiceItem(_groupName); 30 | } 31 | } 32 | 33 | public class RadioChoiceItem : ListBoxItem 34 | { 35 | static RadioChoiceItem() 36 | { 37 | DefaultStyleKeyProperty.OverrideMetadata( 38 | typeof(RadioChoiceItem), 39 | new FrameworkPropertyMetadata(typeof(RadioChoiceItem))); 40 | } 41 | 42 | public RadioChoiceItem(string groupName) 43 | { 44 | GroupName = groupName; 45 | } 46 | 47 | public string GroupName 48 | { 49 | get { return (string)GetValue(GroupNameProperty); } 50 | set { SetValue(GroupNamePropertyKey, value); } 51 | } 52 | 53 | private static readonly DependencyPropertyKey GroupNamePropertyKey = 54 | DependencyProperty.RegisterReadOnly( 55 | "GroupName", 56 | typeof(string), 57 | typeof(RadioChoiceItem), 58 | new PropertyMetadata(null)); 59 | 60 | public static readonly DependencyProperty GroupNameProperty = 61 | GroupNamePropertyKey.DependencyProperty; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/CosmosDBStudio/View/Controls/TreeNodeControl.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls; 3 | using System.Windows.Media; 4 | 5 | namespace CosmosDBStudio.View.Controls 6 | { 7 | public class TreeNodeControl : ContentControl 8 | { 9 | static TreeNodeControl() 10 | { 11 | DefaultStyleKeyProperty.OverrideMetadata( 12 | typeof(TreeNodeControl), 13 | new FrameworkPropertyMetadata(typeof(TreeNodeControl))); 14 | } 15 | 16 | public ImageSource Icon 17 | { 18 | get => (ImageSource)GetValue(IconProperty); 19 | set => SetValue(IconProperty, value); 20 | } 21 | 22 | public static readonly DependencyProperty IconProperty = 23 | DependencyProperty.Register("Icon", typeof(ImageSource), typeof(TreeNodeControl), 24 | new PropertyMetadata(null)); 25 | } 26 | } -------------------------------------------------------------------------------- /src/CosmosDBStudio/View/CosmosTabItem.xaml: -------------------------------------------------------------------------------- 1 |  12 | 13 | 14 | 15 | 16 | 17 |