From d86ff56ce1c0f1ed2b03dd64dbe8548404150ae8 Mon Sep 17 00:00:00 2001 From: Kristen Schau <47155823+krschau@users.noreply.github.com> Date: Wed, 19 Jun 2024 20:04:32 +0100 Subject: [PATCH 01/11] Update widget code to comply with naming rules (#222) --- .editorconfig | 33 ++++++++-- exclusion.dic | 14 ++-- .../DataManager/AzureDataManager.cs | 56 ++++++++-------- src/AzureExtension/DataManager/DataUpdater.cs | 10 +-- .../DataModel/AzureDataStoreSchema.cs | 18 ++--- .../DataModel/DataObjects/Identity.cs | 10 +-- .../DataModel/DataObjects/Organization.cs | 6 +- .../DataModel/DataObjects/Project.cs | 16 ++--- .../DataModel/DataObjects/PullRequests.cs | 16 ++--- .../DataModel/DataObjects/Query.cs | 16 ++--- .../DataModel/DataObjects/WorkItemType.cs | 16 ++--- src/AzureExtension/DataModel/DataStore.cs | 20 +++--- .../DataModel/DataStoreOptions.cs | 8 +-- .../DataModel/DataStoreTransaction.cs | 18 ++--- .../Widgets/AzurePullRequestsWidget.cs | 62 ++++++++--------- .../Widgets/AzureQueryListWidget.cs | 54 +++++++-------- .../Widgets/AzureQueryTilesWidget.cs | 66 +++++++++---------- src/AzureExtension/Widgets/AzureWidget.cs | 8 +-- src/AzureExtension/Widgets/WidgetProvider.cs | 34 +++++----- src/AzureExtension/Widgets/WidgetServer.cs | 6 +- src/Telemetry/Logger.cs | 50 +++++++------- src/Telemetry/LoggerFactory.cs | 14 ++-- test/AzureExtension/Widgets/Icons.cs | 2 - test/AzureExtension/Widgets/Validation.cs | 1 - .../Widgets/WidgetTestsSetup.cs | 6 +- 25 files changed, 291 insertions(+), 269 deletions(-) diff --git a/.editorconfig b/.editorconfig index 5c6eba58..3ea8f60c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -87,16 +87,13 @@ csharp_style_var_elsewhere = true:suggestion csharp_style_var_for_built_in_types = true:suggestion #prefer var when the type is already mentioned on the right-hand side of a declaration expression csharp_style_var_when_type_is_apparent = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:warning #Style - language keyword and framework type options #prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion -#Style - Language rules -csharp_style_implicit_object_creation_when_type_is_apparent = true:warning -csharp_style_var_for_built_in_types = true:warning - #Style - modifier options #prefer accessibility modifiers to be declared except for public interface members. This will currently not differ from always and will act as future proofing for if C# adds default interface methods. @@ -132,6 +129,9 @@ csharp_style_expression_bodied_indexers = true:silent csharp_style_expression_bodied_lambdas = true:silent csharp_style_expression_bodied_local_functions = false:silent +# IDE0290: Use primary constructor +dotnet_diagnostic.IDE0290.severity = silent + [*.{cs,vb}] dotnet_style_operator_placement_when_wrapping = beginning_of_line tab_width = 4 @@ -146,7 +146,6 @@ dotnet_style_collection_initializer = true:suggestion dotnet_style_prefer_simplified_boolean_expressions = true:suggestion dotnet_style_prefer_conditional_expression_over_assignment = true:silent dotnet_style_prefer_conditional_expression_over_return = true:silent -[*.{cs,vb}] #Style - Unnecessary code rules csharp_style_unused_value_assignment_preference = discard_variable:warning @@ -203,10 +202,32 @@ dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion dotnet_style_prefer_compound_assignment = true:warning dotnet_style_prefer_simplified_interpolation = true:suggestion +# Define what we will treat as private fields. +dotnet_naming_symbols.private_fields.applicable_kinds = field +dotnet_naming_symbols.private_fields.applicable_accessibilities = private + +dotnet_naming_symbols.const_private_fields.applicable_kinds = field +dotnet_naming_symbols.const_private_fields.applicable_accessibilities = private +dotnet_naming_symbols.const_private_fields.required_modifiers = const + +# Define rule that something must begin with an underscore and be in camel case. +dotnet_naming_style.require_underscore_prefix_and_camel_case.required_prefix = _ +dotnet_naming_style.require_underscore_prefix_and_camel_case.capitalization = camel_case + +dotnet_naming_style.requre_no_prefix_and_pascal_case.required_prefix = +dotnet_naming_style.requre_no_prefix_and_pascal_case.capitalization = pascal_case + +# Appy our rule to private fields. +dotnet_naming_rule.private_fields_must_begin_with_underscore_and_be_in_camel_case.symbols = private_fields +dotnet_naming_rule.private_fields_must_begin_with_underscore_and_be_in_camel_case.style = require_underscore_prefix_and_camel_case +dotnet_naming_rule.private_fields_must_begin_with_underscore_and_be_in_camel_case.severity = warning + +dotnet_naming_rule.const_fields_must_begin_with_no_prefix_and_be_in_pascal_case.symbols = const_private_fields +dotnet_naming_rule.const_fields_must_begin_with_no_prefix_and_be_in_pascal_case.style = requre_no_prefix_and_pascal_case +dotnet_naming_rule.const_fields_must_begin_with_no_prefix_and_be_in_pascal_case.severity = warning # Spelling spelling_exclusion_path = .\exclusion.dic -spelling_error_severity = warning # Diagnostic configuration diff --git a/exclusion.dic b/exclusion.dic index 35e3f8c1..03059a26 100644 --- a/exclusion.dic +++ b/exclusion.dic @@ -12,10 +12,14 @@ brokerplugin Entra riid enums -enums Stdout -visualstudio -vssps -Entra -riid foobar +msix +yaml +Quickstart +Tensorflow +vscode +codespace +codespaces +dhlog +appsettings diff --git a/src/AzureExtension/DataManager/AzureDataManager.cs b/src/AzureExtension/DataManager/AzureDataManager.cs index c565f670..e4706ceb 100644 --- a/src/AzureExtension/DataManager/AzureDataManager.cs +++ b/src/AzureExtension/DataManager/AzureDataManager.cs @@ -43,16 +43,16 @@ public partial class AzureDataManager : IAzureDataManager, IDisposable public static readonly int QueryResultLimit = 25; // Most data that has not been updated within this time will be removed. - private static readonly TimeSpan DataRetentionTime = TimeSpan.FromDays(1); + private static readonly TimeSpan _dataRetentionTime = TimeSpan.FromDays(1); - private static readonly string LastUpdatedKeyName = "LastUpdated"; + private static readonly string _lastUpdatedKeyName = "LastUpdated"; - private static readonly string Name = nameof(AzureDataManager); + private static readonly string _name = nameof(AzureDataManager); - private static readonly ConcurrentDictionary Instances = new(); + private static readonly ConcurrentDictionary _instances = new(); // Connections are a pairing of DeveloperId and a Uri. - private static readonly ConcurrentDictionary, VssConnection> Connections = new(); + private static readonly ConcurrentDictionary, VssConnection> _connections = new(); private readonly ILogger _log; @@ -74,7 +74,7 @@ public partial class AzureDataManager : IAzureDataManager, IDisposable } catch (Exception e) { - var log = Log.ForContext("SourceContext", Name); + var log = Log.ForContext("SourceContext", _name); log.Error(e, $"Failed creating AzureDataManager for {identifier}"); return null; } @@ -88,7 +88,7 @@ public AzureDataManager(string identifier, DataStoreOptions dataStoreOptions) } InstanceName = identifier; - _log = Log.ForContext("SourceContext", $"{Name}/{InstanceName}"); + _log = Log.ForContext("SourceContext", $"{_name}/{InstanceName}"); DataStoreOptions = dataStoreOptions; DataStore = new DataStore( "DataStore", @@ -111,7 +111,7 @@ public AzureDataManager(string identifier, DataStoreOptions dataStoreOptions) _log.Warning(ex, "Failed setting DeveloperId change handler."); } - if (Instances.TryGetValue(InstanceName, out var instanceIdentifier)) + if (_instances.TryGetValue(InstanceName, out var instanceIdentifier)) { // We should not have duplicate AzureDataManagers, as every client should have one, // but the identifiers may not be unique if using partial Guids. Note in the log @@ -121,7 +121,7 @@ public AzureDataManager(string identifier, DataStoreOptions dataStoreOptions) } else { - Instances.TryAdd(InstanceName, UniqueName); + _instances.TryAdd(InstanceName, UniqueName); } _log.Information($"Created AzureDataManager: {UniqueName}."); @@ -144,7 +144,7 @@ public DateTime LastUpdated get { ValidateDataStore(); - var lastUpdated = MetaData.Get(DataStore, LastUpdatedKeyName); + var lastUpdated = MetaData.Get(DataStore, _lastUpdatedKeyName); if (lastUpdated == null) { return DateTime.MinValue; @@ -154,7 +154,7 @@ public DateTime LastUpdated } } - public override string ToString() => $"{Name}/{InstanceName}"; + public override string ToString() => $"{_name}/{InstanceName}"; public async Task UpdateDataForQueriesAsync(IEnumerable queryUris, string developerLogin, RequestOptions? options = null, Guid? requestor = null) { @@ -710,12 +710,12 @@ private static void SendUpdateEvent(ILogger logger, object? source, DataManagerU // the bad connection issue can be reliably detected and solved. public static ConnectionResult GetConnection(Uri connectionUri, DeveloperId.DeveloperId developerId, bool forceNewConnection = true) { - var log = Log.ForContext("SourceContext", Name); + var log = Log.ForContext("SourceContext", _name); VssConnection? connection; var connectionKey = Tuple.Create(connectionUri, developerId); - if (Connections.ContainsKey(connectionKey)) + if (_connections.ContainsKey(connectionKey)) { - if (Connections.TryGetValue(connectionKey, out connection)) + if (_connections.TryGetValue(connectionKey, out connection)) { // If not forcing a new connection and it has authenticated, reuse it. if (!forceNewConnection && connection.HasAuthenticated) @@ -726,7 +726,7 @@ public static ConnectionResult GetConnection(Uri connectionUri, DeveloperId.Deve else { // Remove the bad connection. - if (Connections.TryRemove(new KeyValuePair, VssConnection>(connectionKey, connection))) + if (_connections.TryRemove(new KeyValuePair, VssConnection>(connectionKey, connection))) { log.Debug($"Removed bad connection to {connectionUri} with {developerId.LoginId}"); } @@ -744,7 +744,7 @@ public static ConnectionResult GetConnection(Uri connectionUri, DeveloperId.Deve var result = AzureClientProvider.CreateVssConnection(connectionUri, developerId); if (result.Result == ResultType.Success) { - if (Connections.TryAdd(connectionKey, result.Connection!)) + if (_connections.TryAdd(connectionKey, result.Connection!)) { log.Debug($"Added connection for {connectionUri} with {developerId.LoginId}"); } @@ -760,10 +760,10 @@ public static ConnectionResult GetConnection(Uri connectionUri, DeveloperId.Deve // Removes unused data from the datastore. private void PruneObsoleteData() { - Query.DeleteBefore(DataStore, DateTime.UtcNow - DataRetentionTime); - PullRequests.DeleteBefore(DataStore, DateTime.UtcNow - DataRetentionTime); - WorkItemType.DeleteBefore(DataStore, DateTime.UtcNow - DataRetentionTime); - Identity.DeleteBefore(DataStore, DateTime.UtcNow - DataRetentionTime); + Query.DeleteBefore(DataStore, DateTime.UtcNow - _dataRetentionTime); + PullRequests.DeleteBefore(DataStore, DateTime.UtcNow - _dataRetentionTime); + WorkItemType.DeleteBefore(DataStore, DateTime.UtcNow - _dataRetentionTime); + Identity.DeleteBefore(DataStore, DateTime.UtcNow - _dataRetentionTime); // The following are not yet pruned, need to ensure there are no current key references // before deletion. @@ -774,7 +774,7 @@ private void PruneObsoleteData() // Sets a last-updated in the MetaData. private void SetLastUpdatedInMetaData() { - MetaData.AddOrUpdate(DataStore, LastUpdatedKeyName, DateTime.Now.ToDataStoreString()); + MetaData.AddOrUpdate(DataStore, _lastUpdatedKeyName, DateTime.Now.ToDataStoreString()); } private void ValidateDataStore() @@ -808,9 +808,9 @@ private void HandleDeveloperIdChange(IDeveloperIdProvider sender, IDeveloperId a // Making the default options a singleton to avoid repeatedly calling the storage APIs and // creating a new AzureDataStoreSchema when not necessary. - private static readonly Lazy LazyDataStoreOptions = new(DefaultOptionsInit); + private static readonly Lazy _lazyDataStoreOptions = new(DefaultOptionsInit); - public static DataStoreOptions DefaultOptions => LazyDataStoreOptions.Value; + public static DataStoreOptions DefaultOptions => _lazyDataStoreOptions.Value; private static DataStoreOptions DefaultOptionsInit() { @@ -821,20 +821,20 @@ private static DataStoreOptions DefaultOptionsInit() }; } - private bool disposed; // To detect redundant calls. + private bool _disposed; // To detect redundant calls. protected virtual void Dispose(bool disposing) { - if (!disposed) + if (!_disposed) { if (disposing) { try { _log.Debug("Disposing of all Disposable resources."); - if (Instances.TryGetValue(InstanceName, out var instanceName) && instanceName == UniqueName) + if (_instances.TryGetValue(InstanceName, out var instanceName) && instanceName == UniqueName) { - Instances.TryRemove(InstanceName, out _); + _instances.TryRemove(InstanceName, out _); _log.Information($"Removed AzureDataManager: {UniqueName}."); } @@ -845,7 +845,7 @@ protected virtual void Dispose(bool disposing) } } - disposed = true; + _disposed = true; } } diff --git a/src/AzureExtension/DataManager/DataUpdater.cs b/src/AzureExtension/DataManager/DataUpdater.cs index e24926a6..71b4440b 100644 --- a/src/AzureExtension/DataManager/DataUpdater.cs +++ b/src/AzureExtension/DataManager/DataUpdater.cs @@ -8,7 +8,7 @@ namespace DevHomeAzureExtension.DataManager; public class DataUpdater : IDisposable { // This is the default interval the timer will run. It is not the interval that we necessarily do work. - private static readonly TimeSpan TimerUpdateInterval = TimeSpan.FromSeconds(5); + private static readonly TimeSpan _timerUpdateInterval = TimeSpan.FromSeconds(5); private readonly ILogger _logger; private readonly PeriodicTimer _timer; @@ -28,7 +28,7 @@ public DataUpdater(TimeSpan interval, Func action) } public DataUpdater(Func action) - : this(TimerUpdateInterval, action) + : this(_timerUpdateInterval, action) { } @@ -62,11 +62,11 @@ public void Stop() public override string ToString() => "DataUpdater"; - private bool disposed; // To detect redundant calls. + private bool _disposed; // To detect redundant calls. protected virtual void Dispose(bool disposing) { - if (!disposed) + if (!_disposed) { _logger.Debug("Disposing of all updater resources."); @@ -75,7 +75,7 @@ protected virtual void Dispose(bool disposing) _timer.Dispose(); } - disposed = true; + _disposed = true; } } diff --git a/src/AzureExtension/DataModel/AzureDataStoreSchema.cs b/src/AzureExtension/DataModel/AzureDataStoreSchema.cs index ffda06ed..04b4bbd6 100644 --- a/src/AzureExtension/DataModel/AzureDataStoreSchema.cs +++ b/src/AzureExtension/DataModel/AzureDataStoreSchema.cs @@ -7,7 +7,7 @@ public class AzureDataStoreSchema : IDataStoreSchema { public long SchemaVersion => SchemaVersionValue; - public List SchemaSqls => SchemaSqlsValue; + public List SchemaSqls => _schemaSqlsValue; public AzureDataStoreSchema() { @@ -16,7 +16,7 @@ public AzureDataStoreSchema() // Update this anytime incompatible changes happen with a released version. private const long SchemaVersionValue = 0x0003; - private static readonly string Metadata = + private const string Metadata = @"CREATE TABLE Metadata (" + "Id INTEGER PRIMARY KEY NOT NULL," + "Key TEXT NOT NULL COLLATE NOCASE," + @@ -24,7 +24,7 @@ public AzureDataStoreSchema() ");" + "CREATE UNIQUE INDEX IDX_Metadata_Key ON Metadata (Key);"; - private static readonly string Identity = + private const string Identity = @"CREATE TABLE Identity (" + "Id INTEGER PRIMARY KEY NOT NULL," + "Name TEXT NOT NULL COLLATE NOCASE," + @@ -36,7 +36,7 @@ public AzureDataStoreSchema() // InternalId is a Guid from the server, so by definition is unique. "CREATE UNIQUE INDEX IDX_Identity_InternalId ON Identity (InternalId);"; - private static readonly string Project = + private const string Project = @"CREATE TABLE Project (" + "Id INTEGER PRIMARY KEY NOT NULL," + "Name TEXT NOT NULL COLLATE NOCASE," + @@ -50,7 +50,7 @@ public AzureDataStoreSchema() // Project Name can be renamed and reused per DevOps documentation, so it is not safe. "CREATE UNIQUE INDEX IDX_Project_InternalId ON Project (InternalId);"; - private static readonly string Organization = + private const string Organization = @"CREATE TABLE Organization (" + "Id INTEGER PRIMARY KEY NOT NULL," + "Name TEXT NOT NULL COLLATE NOCASE," + @@ -65,7 +65,7 @@ public AzureDataStoreSchema() // each connection should correspond to only one organization. "CREATE UNIQUE INDEX IDX_Organization_Connection ON Organization (Connection);"; - private static readonly string Query = + private const string Query = @"CREATE TABLE Query (" + "Id INTEGER PRIMARY KEY NOT NULL," + "QueryId TEXT NOT NULL COLLATE NOCASE," + @@ -82,7 +82,7 @@ public AzureDataStoreSchema() // the query. Therefore we make unique constraint on QueryId AND DeveloperLogin. "CREATE UNIQUE INDEX IDX_Query_QueryIdDeveloperLogin ON Query (QueryId, DeveloperLogin);"; - private static readonly string WorkItemType = + private const string WorkItemType = @"CREATE TABLE WorkItemType (" + "Id INTEGER PRIMARY KEY NOT NULL," + "Name TEXT NOT NULL COLLATE NOCASE," + @@ -97,7 +97,7 @@ public AzureDataStoreSchema() // constraint on Name and ProjectId is consistent and safe. "CREATE UNIQUE INDEX IDX_WorkItemType_NameProjectId ON WorkItemType (Name, ProjectId);"; - private static readonly string PullRequests = + private const string PullRequests = @"CREATE TABLE PullRequests (" + "Id INTEGER PRIMARY KEY NOT NULL," + "RepositoryName TEXT NOT NULL COLLATE NOCASE," + @@ -113,7 +113,7 @@ public AzureDataStoreSchema() "CREATE UNIQUE INDEX IDX_PullRequests_ProjectIdRepositoryNameDeveloperLoginViewId ON PullRequests (ProjectId, RepositoryName, DeveloperLogin, ViewId);"; // All Sqls together. - private static readonly List SchemaSqlsValue = new() + private static readonly List _schemaSqlsValue = new() { Metadata, Identity, diff --git a/src/AzureExtension/DataModel/DataObjects/Identity.cs b/src/AzureExtension/DataModel/DataObjects/Identity.cs index 5795b565..f7ab739b 100644 --- a/src/AzureExtension/DataModel/DataObjects/Identity.cs +++ b/src/AzureExtension/DataModel/DataObjects/Identity.cs @@ -25,11 +25,11 @@ public class Identity // it reflected in the datastore. We expect identity data to change very infrequently. // Since updating an identity involves re-fetching an avatar image, we want to do this // infrequently. - private static readonly long UpdateThreshold = TimeSpan.FromDays(3).Ticks; + private static readonly long _updateThreshold = TimeSpan.FromDays(3).Ticks; // Avatars may fail to download, in this case we will retry more frequently than the normal // update threshold since this can be intermittent. - private static readonly long AvatarRetryDelay = TimeSpan.FromHours(1).Ticks; + private static readonly long _avatarRetryDelay = TimeSpan.FromHours(1).Ticks; [Key] [JsonIgnore] @@ -178,11 +178,11 @@ public static Identity GetOrCreateIdentity(DataStore dataStore, IdentityRef? ide } // Check for whether we need to update the record. - // We don't want to create an identity object and download a new avatar unlesss it needs to + // We don't want to create an identity object and download a new avatar unless it needs to // be updated. In the event of an empty avatar we will retry more frequently to update it, // but not every time. - if (existing is null || ((DateTime.Now.Ticks - existing.TimeUpdated) > UpdateThreshold) - || (string.IsNullOrEmpty(existing.Avatar) && ((DateTime.Now.Ticks - existing.TimeUpdated) > AvatarRetryDelay))) + if (existing is null || ((DateTime.Now.Ticks - existing.TimeUpdated) > _updateThreshold) + || (string.IsNullOrEmpty(existing.Avatar) && ((DateTime.Now.Ticks - existing.TimeUpdated) > _avatarRetryDelay))) { var newIdentity = CreateFromIdentityRef(identityRef, connection); return AddOrUpdateIdentity(dataStore, newIdentity); diff --git a/src/AzureExtension/DataModel/DataObjects/Organization.cs b/src/AzureExtension/DataModel/DataObjects/Organization.cs index 10417258..6b3b2a7a 100644 --- a/src/AzureExtension/DataModel/DataObjects/Organization.cs +++ b/src/AzureExtension/DataModel/DataObjects/Organization.cs @@ -14,7 +14,7 @@ public class Organization // This is the time between seeing a potential updated Organization record and updating it. // This value / 2 is the average time between Organization updating their Organization data and // having it reflected in the datastore. - private static readonly long UpdateThreshold = TimeSpan.FromHours(4).Ticks; + private static readonly long _updateThreshold = TimeSpan.FromHours(4).Ticks; [Key] public long Id { get; set; } = DataStore.NoForeignKey; @@ -32,7 +32,7 @@ public class Organization [Write(false)] [System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Will be used in near-future changes.")] - private DataStore? DataStore { get; set; } + private DataStore? DataStore { get; set; } [Write(false)] [Computed] @@ -70,7 +70,7 @@ public static Organization AddOrUpdateOrganization(DataStore dataStore, Organiza // avoid unnecessary updating and database operations for data that // is extremely unlikely to have changed in any significant way, we // will only update every UpdateThreshold amount of time. - if ((organization.TimeUpdated - existingOrganization.TimeUpdated) > UpdateThreshold) + if ((organization.TimeUpdated - existingOrganization.TimeUpdated) > _updateThreshold) { organization.Id = existingOrganization.Id; dataStore.Connection!.Update(organization); diff --git a/src/AzureExtension/DataModel/DataObjects/Project.cs b/src/AzureExtension/DataModel/DataObjects/Project.cs index d2376547..f38e069b 100644 --- a/src/AzureExtension/DataModel/DataObjects/Project.cs +++ b/src/AzureExtension/DataModel/DataObjects/Project.cs @@ -4,22 +4,22 @@ using Dapper; using Dapper.Contrib.Extensions; using DevHomeAzureExtension.Helpers; -using Microsoft.TeamFoundation.Core.WebApi; +using Microsoft.TeamFoundation.Core.WebApi; using Serilog; namespace DevHomeAzureExtension.DataModel; [Table("Project")] public class Project -{ - private static readonly Lazy _log = new(() => Serilog.Log.ForContext("SourceContext", $"DataModel/{nameof(Project)}")); - - private static readonly ILogger Log = _log.Value; +{ + private static readonly Lazy _log = new(() => Serilog.Log.ForContext("SourceContext", $"DataModel/{nameof(Project)}")); + + private static readonly ILogger Log = _log.Value; // This is the time between seeing a potential updated Project record and updating it. // This value / 2 is the average time between Project updating their Project data and having // it reflected in the datastore. - private static readonly long UpdateThreshold = TimeSpan.FromHours(4).Ticks; + private static readonly long _updateThreshold = TimeSpan.FromHours(4).Ticks; [Key] public long Id { get; set; } = DataStore.NoForeignKey; @@ -37,7 +37,7 @@ public class Project public long TimeUpdated { get; set; } = DataStore.NoForeignKey; [Write(false)] - private DataStore? DataStore { get; set; } + private DataStore? DataStore { get; set; } [Write(false)] [Computed] @@ -78,7 +78,7 @@ public static Project AddOrUpdateProject(DataStore dataStore, Project project) // avoid unnecessary updating and database operations for data that // is extremely unlikely to have changed in any significant way, we // will only update every UpdateThreshold amount of time. - if ((project.TimeUpdated - existingProject.TimeUpdated) > UpdateThreshold) + if ((project.TimeUpdated - existingProject.TimeUpdated) > _updateThreshold) { project.Id = existingProject.Id; dataStore.Connection!.Update(project); diff --git a/src/AzureExtension/DataModel/DataObjects/PullRequests.cs b/src/AzureExtension/DataModel/DataObjects/PullRequests.cs index 860facb7..22432861 100644 --- a/src/AzureExtension/DataModel/DataObjects/PullRequests.cs +++ b/src/AzureExtension/DataModel/DataObjects/PullRequests.cs @@ -3,7 +3,7 @@ using Dapper; using Dapper.Contrib.Extensions; -using DevHomeAzureExtension.Helpers; +using DevHomeAzureExtension.Helpers; using Serilog; using DevId = DevHomeAzureExtension.DeveloperId; @@ -11,13 +11,13 @@ namespace DevHomeAzureExtension.DataModel; [Table("PullRequests")] public class PullRequests -{ - private static readonly Lazy _log = new(() => Serilog.Log.ForContext("SourceContext", $"DataModel/{nameof(PullRequests)}")); - - private static readonly ILogger Log = _log.Value; +{ + private static readonly Lazy _log = new(() => Serilog.Log.ForContext("SourceContext", $"DataModel/{nameof(PullRequests)}")); + + private static readonly ILogger Log = _log.Value; // This is the time between seeing a search and updating it's TimeUpdated. - private static readonly long UpdateThreshold = TimeSpan.FromMinutes(2).Ticks; + private static readonly long _updateThreshold = TimeSpan.FromMinutes(2).Ticks; [Key] public long Id { get; set; } = DataStore.NoForeignKey; @@ -39,7 +39,7 @@ public class PullRequests public override string ToString() => DeveloperLogin + "/" + RepositoryName; [Write(false)] - private DataStore? DataStore { get; set; } + private DataStore? DataStore { get; set; } [Write(false)] [Computed] @@ -76,7 +76,7 @@ private static PullRequests AddOrUpdate(DataStore dataStore, PullRequests pullRe if (existing is not null) { // Update threshold is in case there are many requests in a short period of time. - if ((pullRequests.TimeUpdated - existing.TimeUpdated) > UpdateThreshold) + if ((pullRequests.TimeUpdated - existing.TimeUpdated) > _updateThreshold) { pullRequests.Id = existing.Id; dataStore.Connection!.Update(pullRequests); diff --git a/src/AzureExtension/DataModel/DataObjects/Query.cs b/src/AzureExtension/DataModel/DataObjects/Query.cs index f9cfd035..4c2befd1 100644 --- a/src/AzureExtension/DataModel/DataObjects/Query.cs +++ b/src/AzureExtension/DataModel/DataObjects/Query.cs @@ -3,7 +3,7 @@ using Dapper; using Dapper.Contrib.Extensions; -using DevHomeAzureExtension.Helpers; +using DevHomeAzureExtension.Helpers; using Serilog; using DevId = DevHomeAzureExtension.DeveloperId; @@ -11,13 +11,13 @@ namespace DevHomeAzureExtension.DataModel; [Table("Query")] public class Query -{ - private static readonly Lazy _log = new(() => Serilog.Log.ForContext("SourceContext", $"DataModel/{nameof(Query)}")); - - private static readonly ILogger Log = _log.Value; +{ + private static readonly Lazy _log = new(() => Serilog.Log.ForContext("SourceContext", $"DataModel/{nameof(Query)}")); + + private static readonly ILogger Log = _log.Value; // This is the time between seeing a search and updating it's TimeUpdated. - private static readonly long UpdateThreshold = TimeSpan.FromMinutes(2).Ticks; + private static readonly long _updateThreshold = TimeSpan.FromMinutes(2).Ticks; [Key] public long Id { get; set; } = DataStore.NoForeignKey; @@ -44,7 +44,7 @@ public class Query public override string ToString() => QueryId; [Write(false)] - private DataStore? DataStore { get; set; } + private DataStore? DataStore { get; set; } [Write(false)] [Computed] @@ -77,7 +77,7 @@ private static Query AddOrUpdate(DataStore dataStore, Query query) var existing = Get(dataStore, query.QueryId, query.DeveloperLogin); if (existing is not null) { - if ((query.TimeUpdated - existing.TimeUpdated) > UpdateThreshold) + if ((query.TimeUpdated - existing.TimeUpdated) > _updateThreshold) { query.Id = existing.Id; dataStore.Connection!.Update(query); diff --git a/src/AzureExtension/DataModel/DataObjects/WorkItemType.cs b/src/AzureExtension/DataModel/DataObjects/WorkItemType.cs index 33ea63e4..fa560e3d 100644 --- a/src/AzureExtension/DataModel/DataObjects/WorkItemType.cs +++ b/src/AzureExtension/DataModel/DataObjects/WorkItemType.cs @@ -5,7 +5,7 @@ using System.Text.Json.Serialization; using Dapper; using Dapper.Contrib.Extensions; -using DevHomeAzureExtension.Helpers; +using DevHomeAzureExtension.Helpers; using Serilog; // This is to resolve ambiguity between DataModel WorkItemType and @@ -16,15 +16,15 @@ namespace DevHomeAzureExtension.DataModel; [Table("WorkItemType")] public class WorkItemType -{ - private static readonly Lazy _log = new(() => Serilog.Log.ForContext("SourceContext", $"DataModel/{nameof(WorkItemType)}")); - - private static readonly ILogger Log = _log.Value; +{ + private static readonly Lazy _log = new(() => Serilog.Log.ForContext("SourceContext", $"DataModel/{nameof(WorkItemType)}")); + + private static readonly ILogger Log = _log.Value; // This is the time between seeing a potential updated WorkItemType record and updating it. // This value / 2 is the average time between WorkItemType updating their WorkItemType data // and having it reflected in the datastore. - private static readonly long UpdateThreshold = TimeSpan.FromHours(4).Ticks; + private static readonly long _updateThreshold = TimeSpan.FromHours(4).Ticks; [Key] public long Id { get; set; } = DataStore.NoForeignKey; @@ -54,7 +54,7 @@ public class WorkItemType [Write(false)] [JsonIgnore] - private DataStore? DataStore { get; set; } + private DataStore? DataStore { get; set; } [Write(false)] [Computed] @@ -110,7 +110,7 @@ public static WorkItemType AddOrUpdateWorkItemType(DataStore dataStore, WorkItem // avoid unnecessary updating and database operations for data that // is extremely unlikely to have changed in any significant way, we // will only update every UpdateThreshold amount of time. - if ((workItemType.TimeUpdated - existingWorkItemType.TimeUpdated) > UpdateThreshold) + if ((workItemType.TimeUpdated - existingWorkItemType.TimeUpdated) > _updateThreshold) { workItemType.Id = existingWorkItemType.Id; dataStore.Connection!.Update(workItemType); diff --git a/src/AzureExtension/DataModel/DataStore.cs b/src/AzureExtension/DataModel/DataStore.cs index fe642201..c19cfc2f 100644 --- a/src/AzureExtension/DataModel/DataStore.cs +++ b/src/AzureExtension/DataModel/DataStore.cs @@ -22,7 +22,7 @@ public DataStore(string name, string dataStoreFilePath, IDataStoreSchema schema) { Name = name; DataStoreFilePath = dataStoreFilePath; - this.schema = schema; + _schema = schema; _log = Log.ForContext("SourceContext", Name); } @@ -36,7 +36,7 @@ public string DataStoreFilePath private set; } - private readonly IDataStoreSchema schema; + private readonly IDataStoreSchema _schema; public bool Create(bool deleteExistingDatabase = false) { @@ -50,14 +50,14 @@ public bool Create(bool deleteExistingDatabase = false) { Open(); var currentSchemaVersion = GetPragma("user_version"); - if (currentSchemaVersion != schema.SchemaVersion) + if (currentSchemaVersion != _schema.SchemaVersion) { // Any mismatch of schema is considered invalid. // Since the data stored is functionally a cache, the simplest and most reliable. // migration method is to delete the existing database and create anew. deleteExistingDatabase = true; Close(); - _log.Information($"Schema mismatch. Expected: {schema.SchemaVersion} Actual: {currentSchemaVersion}"); + _log.Information($"Schema mismatch. Expected: {_schema.SchemaVersion} Actual: {currentSchemaVersion}"); } } catch (SqliteException e) @@ -149,7 +149,7 @@ public void Open() } _log.Debug($"Opening datastore {DataStoreFilePath}"); - disposed = false; + _disposed = false; var builder = new SqliteConnectionStringBuilder { DataSource = DataStoreFilePath, @@ -178,14 +178,14 @@ private void CreateSchema() SetPragma("encoding", "\"UTF-8\""); using var tx = BeginTransaction(); - var sqls = schema.SchemaSqls; + var sqls = _schema.SchemaSqls; foreach (var sql in sqls) { Execute(sql); } _log.Debug($"Created schema ({sqls.Count} entities)"); - SetPragma("user_version", schema.SchemaVersion); + SetPragma("user_version", _schema.SchemaVersion); tx.Commit(); } @@ -294,18 +294,18 @@ public void Close() } } - private bool disposed; // To detect redundant calls. + private bool _disposed; // To detect redundant calls. protected virtual void Dispose(bool disposing) { - if (!disposed) + if (!_disposed) { if (disposing) { Close(); } - disposed = true; + _disposed = true; } } diff --git a/src/AzureExtension/DataModel/DataStoreOptions.cs b/src/AzureExtension/DataModel/DataStoreOptions.cs index 4b654505..18588fc9 100644 --- a/src/AzureExtension/DataModel/DataStoreOptions.cs +++ b/src/AzureExtension/DataModel/DataStoreOptions.cs @@ -13,15 +13,15 @@ public partial class DataStoreOptions // If we directly put in the ApplicationData folder, it would fail anytime the program was not packaged. // For use with packaged application, set in Options to: // ApplicationData.Current.LocalFolder.Path - private readonly string dataStoreFolderPathDefault = Path.Combine(Path.GetTempPath(), "AzureExtension"); + private readonly string _dataStoreFolderPathDefault = Path.Combine(Path.GetTempPath(), "AzureExtension"); // ApplicationData is not static, using a static folder for initialization. - private string? dataStoreFolderPath; + private string? _dataStoreFolderPath; public string DataStoreFolderPath { - get => dataStoreFolderPath is null ? dataStoreFolderPathDefault : dataStoreFolderPath; - set => dataStoreFolderPath = string.IsNullOrEmpty(value) ? dataStoreFolderPathDefault : value; + get => _dataStoreFolderPath is null ? _dataStoreFolderPathDefault : _dataStoreFolderPath; + set => _dataStoreFolderPath = string.IsNullOrEmpty(value) ? _dataStoreFolderPathDefault : value; } public IDataStoreSchema? DataStoreSchema { get; set; } diff --git a/src/AzureExtension/DataModel/DataStoreTransaction.cs b/src/AzureExtension/DataModel/DataStoreTransaction.cs index cf52b50b..3c2f3b1a 100644 --- a/src/AzureExtension/DataModel/DataStoreTransaction.cs +++ b/src/AzureExtension/DataModel/DataStoreTransaction.cs @@ -7,7 +7,7 @@ namespace DevHomeAzureExtension.DataModel; public class DataStoreTransaction : IDataStoreTransaction { - private SqliteTransaction? transaction; + private SqliteTransaction? _transaction; public static IDataStoreTransaction BeginTransaction(DataStore dataStore) { @@ -24,32 +24,32 @@ public static IDataStoreTransaction BeginTransaction(DataStore dataStore) private DataStoreTransaction(SqliteTransaction? tx) { - transaction = tx; + _transaction = tx; } public void Commit() { - transaction?.Commit(); + _transaction?.Commit(); } public void Rollback() { - transaction?.Rollback(); + _transaction?.Rollback(); } - private bool disposed; // To detect redundant calls. + private bool _disposed; // To detect redundant calls. protected virtual void Dispose(bool disposing) { - if (!disposed) + if (!_disposed) { if (disposing) { - transaction?.Dispose(); - transaction = null; + _transaction?.Dispose(); + _transaction = null; } - disposed = true; + _disposed = true; } } diff --git a/src/AzureExtension/Widgets/AzurePullRequestsWidget.cs b/src/AzureExtension/Widgets/AzurePullRequestsWidget.cs index 6f7c8605..640b7d2d 100644 --- a/src/AzureExtension/Widgets/AzurePullRequestsWidget.cs +++ b/src/AzureExtension/Widgets/AzurePullRequestsWidget.cs @@ -14,16 +14,16 @@ namespace DevHomeAzureExtension.Widgets; internal sealed class AzurePullRequestsWidget : AzureWidget { - private readonly string sampleIconData = IconLoader.GetIconAsBase64("screenshot.png"); + private readonly string _sampleIconData = IconLoader.GetIconAsBase64("screenshot.png"); - private static readonly string DefaultSelectedView = "Mine"; + private const string DefaultSelectedView = "Mine"; // Widget Data - private string widgetTitle = string.Empty; - private string selectedRepositoryUrl = string.Empty; - private string selectedRepositoryName = string.Empty; - private string selectedView = DefaultSelectedView; - private string? message; + private string _widgetTitle = string.Empty; + private string _selectedRepositoryUrl = string.Empty; + private string _selectedRepositoryName = string.Empty; + private string _selectedView = DefaultSelectedView; + private string? _message; // Creation and destruction methods public AzurePullRequestsWidget() @@ -82,13 +82,13 @@ protected override bool ValidateConfiguration(WidgetActionInvokedArgs args) Page = WidgetPageState.Configure; var data = args.Data; var dataObject = JsonObject.Parse(data); - message = null; + _message = null; if (dataObject != null && dataObject["account"] != null && dataObject["query"] != null) { - widgetTitle = dataObject["widgetTitle"]?.GetValue() ?? string.Empty; + _widgetTitle = dataObject["widgetTitle"]?.GetValue() ?? string.Empty; DeveloperLoginId = dataObject["account"]?.GetValue() ?? string.Empty; - selectedRepositoryUrl = dataObject["query"]?.GetValue() ?? string.Empty; - selectedView = dataObject["view"]?.GetValue() ?? string.Empty; + _selectedRepositoryUrl = dataObject["query"]?.GetValue() ?? string.Empty; + _selectedView = dataObject["view"]?.GetValue() ?? string.Empty; SetDefaultDeveloperLoginId(); if (DeveloperLoginId != dataObject["account"]?.GetValue()) { @@ -101,15 +101,15 @@ protected override bool ValidateConfiguration(WidgetActionInvokedArgs args) var developerId = GetDevId(DeveloperLoginId); if (developerId == null) { - message = Resources.GetResource(@"Widget_Template/DevIDError"); + _message = Resources.GetResource(@"Widget_Template/DevIDError"); UpdateActivityState(); return false; } - var repositoryInfo = AzureClientHelpers.GetRepositoryInfo(selectedRepositoryUrl, developerId); + var repositoryInfo = AzureClientHelpers.GetRepositoryInfo(_selectedRepositoryUrl, developerId); if (repositoryInfo.Result != ResultType.Success) { - message = GetMessageForError(repositoryInfo.Error, repositoryInfo.ErrorMessage); + _message = GetMessageForError(repositoryInfo.Error, repositoryInfo.ErrorMessage); UpdateActivityState(); return false; } @@ -137,7 +137,7 @@ public override void OnCustomizationRequested(WidgetCustomizationRequestedArgs c protected override void SetDefaultDeveloperLoginId() { base.SetDefaultDeveloperLoginId(); - var azureOrg = new AzureUri(selectedRepositoryUrl).Organization; + var azureOrg = new AzureUri(_selectedRepositoryUrl).Organization; if (!string.IsNullOrEmpty(azureOrg)) { var devIds = DeveloperIdProvider.GetInstance().GetLoggedInDeveloperIds().DeveloperIds; @@ -186,8 +186,8 @@ public override void RequestContentData() try { var requestOptions = RequestOptions.RequestOptionsDefault(); - var azureUri = new AzureUri(selectedRepositoryUrl); - DataManager!.UpdateDataForPullRequestsAsync(azureUri, developerId.LoginId, GetPullRequestView(selectedView), requestOptions, new Guid(Id)); + var azureUri = new AzureUri(_selectedRepositoryUrl); + DataManager!.UpdateDataForPullRequestsAsync(azureUri, developerId.LoginId, GetPullRequestView(_selectedView), requestOptions, new Guid(Id)); } catch (Exception ex) { @@ -204,11 +204,11 @@ protected override void ResetDataFromState(string data) return; } - widgetTitle = dataObject["widgetTitle"]?.GetValue() ?? string.Empty; + _widgetTitle = dataObject["widgetTitle"]?.GetValue() ?? string.Empty; DeveloperLoginId = dataObject["account"]?.GetValue() ?? string.Empty; - selectedRepositoryUrl = dataObject["query"]?.GetValue() ?? string.Empty; - selectedView = dataObject["view"]?.GetValue() ?? string.Empty; - message = null; + _selectedRepositoryUrl = dataObject["query"]?.GetValue() ?? string.Empty; + _selectedView = dataObject["view"]?.GetValue() ?? string.Empty; + _message = null; var developerId = GetDevId(DeveloperLoginId); if (developerId == null) @@ -216,8 +216,8 @@ protected override void ResetDataFromState(string data) return; } - var azureUri = new AzureUri(selectedRepositoryUrl); - selectedRepositoryName = azureUri.Repository; + var azureUri = new AzureUri(_selectedRepositoryUrl); + _selectedRepositoryName = azureUri.Repository; } public override string GetConfiguration(string data) @@ -239,10 +239,10 @@ public override string GetConfiguration(string data) configurationData.Add("accounts", developerIdsData); configurationData.Add("selectedDevId", DeveloperLoginId); - configurationData.Add("url", selectedRepositoryUrl); - configurationData.Add("selectedView", selectedView); - configurationData.Add("message", message); - configurationData.Add("widgetTitle", widgetTitle); + configurationData.Add("url", _selectedRepositoryUrl); + configurationData.Add("selectedView", _selectedView); + configurationData.Add("message", _message); + configurationData.Add("widgetTitle", _widgetTitle); configurationData.Add("pinned", Pinned); configurationData.Add("arrow", IconLoader.GetIconAsBase64("arrow.png")); @@ -262,17 +262,17 @@ public override void LoadContentData() return; } - var azureUri = new AzureUri(selectedRepositoryUrl); + var azureUri = new AzureUri(_selectedRepositoryUrl); if (!azureUri.IsRepository) { - Log.Error($"Invalid Uri: {selectedRepositoryUrl}"); + Log.Error($"Invalid Uri: {_selectedRepositoryUrl}"); return; } PullRequests? pullRequests; // This can throw if DataStore is not connected. - pullRequests = DataManager!.GetPullRequests(azureUri, developerId.LoginId, GetPullRequestView(selectedView)); + pullRequests = DataManager!.GetPullRequests(azureUri, developerId.LoginId, GetPullRequestView(_selectedView)); var pullRequestsResults = pullRequests is null ? new Dictionary() : JsonConvert.DeserializeObject>(pullRequests.Results); @@ -308,7 +308,7 @@ public override void LoadContentData() itemsData.Add("maxItemsDisplayed", AzureDataManager.PullRequestResultLimit); itemsData.Add("items", itemsArray); - itemsData.Add("widgetTitle", widgetTitle); + itemsData.Add("widgetTitle", _widgetTitle); itemsData.Add("is_loading_data", DataState == WidgetDataState.Unknown); ContentData = itemsData.ToJsonString(); diff --git a/src/AzureExtension/Widgets/AzureQueryListWidget.cs b/src/AzureExtension/Widgets/AzureQueryListWidget.cs index ae7f76e3..811f60ff 100644 --- a/src/AzureExtension/Widgets/AzureQueryListWidget.cs +++ b/src/AzureExtension/Widgets/AzureQueryListWidget.cs @@ -13,13 +13,13 @@ namespace DevHomeAzureExtension.Widgets; internal sealed class AzureQueryListWidget : AzureWidget { - private readonly string sampleIconData = IconLoader.GetIconAsBase64("screenshot.png"); + private readonly string _sampleIconData = IconLoader.GetIconAsBase64("screenshot.png"); // Widget Data - private string widgetTitle = string.Empty; - private string selectedQueryUrl = string.Empty; - private string selectedQueryId = string.Empty; - private string? message; + private string _widgetTitle = string.Empty; + private string _selectedQueryUrl = string.Empty; + private string _selectedQueryId = string.Empty; + private string? _message; // Creation and destruction methods public AzureQueryListWidget() @@ -78,13 +78,13 @@ protected override bool ValidateConfiguration(WidgetActionInvokedArgs args) Page = WidgetPageState.Configure; var data = args.Data; var dataObject = JsonObject.Parse(data); - message = null; + _message = null; if (dataObject != null && dataObject["account"] != null && dataObject["query"] != null) { CanSave = false; - widgetTitle = dataObject["widgetTitle"]?.GetValue() ?? string.Empty; - selectedQueryUrl = dataObject["query"]?.GetValue() ?? string.Empty; + _widgetTitle = dataObject["widgetTitle"]?.GetValue() ?? string.Empty; + _selectedQueryUrl = dataObject["query"]?.GetValue() ?? string.Empty; DeveloperLoginId = dataObject["account"]?.GetValue() ?? string.Empty; SetDefaultDeveloperLoginId(); if (DeveloperLoginId != dataObject["account"]?.GetValue()) @@ -98,16 +98,16 @@ protected override bool ValidateConfiguration(WidgetActionInvokedArgs args) var developerId = GetDevId(DeveloperLoginId); if (developerId == null) { - message = Resources.GetResource(@"Widget_Template/DevIDError"); + _message = Resources.GetResource(@"Widget_Template/DevIDError"); UpdateActivityState(); return false; } - var queryInfo = AzureClientHelpers.GetQueryInfo(selectedQueryUrl, developerId); - selectedQueryId = queryInfo.AzureUri.Query; // This will be empty string if invalid query. + var queryInfo = AzureClientHelpers.GetQueryInfo(_selectedQueryUrl, developerId); + _selectedQueryId = queryInfo.AzureUri.Query; // This will be empty string if invalid query. if (queryInfo.Result != ResultType.Success) { - message = GetMessageForError(queryInfo.Error, queryInfo.ErrorMessage); + _message = GetMessageForError(queryInfo.Error, queryInfo.ErrorMessage); UpdateActivityState(); return false; } @@ -115,9 +115,9 @@ protected override bool ValidateConfiguration(WidgetActionInvokedArgs args) { CanSave = true; Pinned = true; - if (string.IsNullOrEmpty(widgetTitle)) + if (string.IsNullOrEmpty(_widgetTitle)) { - widgetTitle = queryInfo.Name; + _widgetTitle = queryInfo.Name; } Page = WidgetPageState.Content; @@ -143,7 +143,7 @@ protected override void SetDefaultDeveloperLoginId() { base.SetDefaultDeveloperLoginId(); - var azureOrg = new AzureUri(selectedQueryUrl).Organization; + var azureOrg = new AzureUri(_selectedQueryUrl).Organization; if (!string.IsNullOrEmpty(azureOrg)) { var devIds = DeveloperIdProvider.GetInstance().GetLoggedInDeveloperIds().DeveloperIds; @@ -193,7 +193,7 @@ public override void RequestContentData() try { - var azureUri = new AzureUri(selectedQueryUrl); + var azureUri = new AzureUri(_selectedQueryUrl); DataManager!.UpdateDataForQueryAsync(azureUri, developerId.LoginId, requestOptions, new Guid(Id)); } catch (Exception ex) @@ -211,10 +211,10 @@ protected override void ResetDataFromState(string data) return; } - widgetTitle = dataObject["widgetTitle"]?.GetValue() ?? string.Empty; + _widgetTitle = dataObject["widgetTitle"]?.GetValue() ?? string.Empty; DeveloperLoginId = dataObject["account"]?.GetValue() ?? string.Empty; - selectedQueryUrl = dataObject["query"]?.GetValue() ?? string.Empty; - message = null; + _selectedQueryUrl = dataObject["query"]?.GetValue() ?? string.Empty; + _message = null; var developerId = GetDevId(DeveloperLoginId); if (developerId == null) @@ -222,8 +222,8 @@ protected override void ResetDataFromState(string data) return; } - var azureUri = new AzureUri(selectedQueryUrl); - selectedQueryId = azureUri.Query; + var azureUri = new AzureUri(_selectedQueryUrl); + _selectedQueryId = azureUri.Query; if (!azureUri.IsQuery) { Log.Error("Selected Query Url from ResetDataFromState is not a valid query."); @@ -249,9 +249,9 @@ public override string GetConfiguration(string data) configurationData.Add("accounts", developerIdsData); configurationData.Add("selectedDevId", DeveloperLoginId); - configurationData.Add("url", selectedQueryUrl); - configurationData.Add("message", message); - configurationData.Add("widgetTitle", widgetTitle); + configurationData.Add("url", _selectedQueryUrl); + configurationData.Add("message", _message); + configurationData.Add("widgetTitle", _widgetTitle); configurationData.Add("pinned", Pinned); configurationData.Add("arrow", IconLoader.GetIconAsBase64("arrow.png")); @@ -273,12 +273,12 @@ public override void LoadContentData() return; } - var azureUri = new AzureUri(selectedQueryUrl); + var azureUri = new AzureUri(_selectedQueryUrl); if (!azureUri.IsQuery) { // This should never happen. Already was validated on configuration. - Log.Error($"Invalid Uri: {selectedQueryUrl}"); + Log.Error($"Invalid Uri: {_selectedQueryUrl}"); return; } @@ -321,7 +321,7 @@ public override void LoadContentData() itemsData.Add("workItemCount", queryInfo is null ? 0 : (int)queryInfo.QueryResultCount); itemsData.Add("maxItemsDisplayed", AzureDataManager.QueryResultLimit); itemsData.Add("items", itemsArray); - itemsData.Add("widgetTitle", widgetTitle); + itemsData.Add("widgetTitle", _widgetTitle); itemsData.Add("is_loading_data", DataState == WidgetDataState.Unknown); ContentData = itemsData.ToJsonString(); diff --git a/src/AzureExtension/Widgets/AzureQueryTilesWidget.cs b/src/AzureExtension/Widgets/AzureQueryTilesWidget.cs index 663d1277..cf677f43 100644 --- a/src/AzureExtension/Widgets/AzureQueryTilesWidget.cs +++ b/src/AzureExtension/Widgets/AzureQueryTilesWidget.cs @@ -13,13 +13,13 @@ namespace DevHomeAzureExtension.Widgets; internal sealed class AzureQueryTilesWidget : AzureWidget { - private readonly string sampleIconData = IconLoader.GetIconAsBase64("screenshot.png"); + private readonly string _sampleIconData = IconLoader.GetIconAsBase64("screenshot.png"); - private readonly int maxNumLines = 3; - private readonly int maxNumColumns = 2; + private readonly int _maxNumLines = 3; + private readonly int _maxNumColumns = 2; // Widget data - private readonly List tiles = []; + private readonly List _tiles = []; // Creation and destruction methods public AzureQueryTilesWidget() @@ -39,7 +39,7 @@ public override void CreateWidget(WidgetContext widgetContext, string state) else { // Start with one initial tile. - tiles.Add(new QueryTile( + _tiles.Add(new QueryTile( string.Empty, string.Empty)); } @@ -60,7 +60,7 @@ private void HandleAddTile(WidgetActionInvokedArgs args) UpdateAllTiles(args.Data); - tiles.Add(new QueryTile( + _tiles.Add(new QueryTile( string.Empty, string.Empty)); @@ -74,8 +74,8 @@ private string RemoveLastTileFromData(string data) { var dataObject = JsonObject.Parse(data)!.AsObject(); - dataObject.Remove($"tileTitle{tiles.Count}"); - dataObject.Remove($"query{tiles.Count}"); + dataObject.Remove($"tileTitle{_tiles.Count}"); + dataObject.Remove($"query{_tiles.Count}"); return dataObject.ToJsonString(); } @@ -85,9 +85,9 @@ private void HandleRemoveTile(WidgetActionInvokedArgs args) Page = WidgetPageState.Loading; UpdateWidget(); - if (tiles.Count != 0) + if (_tiles.Count != 0) { - tiles.RemoveAt(tiles.Count - 1); + _tiles.RemoveAt(_tiles.Count - 1); } UpdateAllTiles(RemoveLastTileFromData(args.Data)); @@ -208,7 +208,7 @@ public override void RequestContentData() } var queryList = new List(); - foreach (var tile in tiles) + foreach (var tile in _tiles) { queryList.Add(tile.AzureUri); } @@ -247,23 +247,23 @@ public override void LoadContentData() return; } - for (var i = 0; i < maxNumLines; ++i) + for (var i = 0; i < _maxNumLines; ++i) { var tilesArray = new JsonArray(); - for (var j = 0; j < maxNumColumns; ++j) + for (var j = 0; j < _maxNumColumns; ++j) { var pos = (2 * i) + j; - if (pos < tiles.Count) + if (pos < _tiles.Count) { var workItemCount = 0; try { - var queryInfo = DataManager!.GetQuery(tiles[pos].AzureUri.Query, developerId.LoginId); + var queryInfo = DataManager!.GetQuery(_tiles[pos].AzureUri.Query, developerId.LoginId); if (queryInfo == null) { // Failing this query doesn't mean the other queries won't fail. - Log.Information($"No query information found for query: {tiles[pos].AzureUri.Query}"); + Log.Information($"No query information found for query: {_tiles[pos].AzureUri.Query}"); } else { @@ -272,10 +272,10 @@ public override void LoadContentData() var tile = new JsonObject { - { "title", tiles[pos].Title }, + { "title", _tiles[pos].Title }, { "counter", workItemCount }, { "backgroundImage", IconLoader.GetIconAsBase64("BlueBackground.png") }, - { "url", tiles[pos].AzureUri.ToString() }, + { "url", _tiles[pos].AzureUri.ToString() }, }; tilesArray.Add(tile); @@ -312,14 +312,14 @@ private bool ValidateConfigurationData() return false; } - if (tiles.Count == 0) + if (_tiles.Count == 0) { return false; } - for (var i = 0; i < tiles.Count; ++i) + for (var i = 0; i < _tiles.Count; ++i) { - var tile = tiles[i]; + var tile = _tiles[i]; if (tile.Validated) { // Skip tiles that have already been validated. @@ -343,7 +343,7 @@ private bool ValidateConfigurationData() tile.Title = queryInfo.Name; } - tiles[i] = tile; + _tiles[i] = tile; } CanSave = !pinIssueFound; @@ -360,13 +360,13 @@ private void ResetNumberOfTilesFromData(string data) return; } - tiles.Clear(); + _tiles.Clear(); for (var i = 0; i < 6; ++i) { if (dataObject[$"query{i}"] != null && dataObject[$"tileTitle{i}"] != null) { - tiles.Add(new QueryTile()); + _tiles.Add(new QueryTile()); } } } @@ -376,7 +376,7 @@ private void ResetNumberOfTilesFromData(string data) protected override void SetDefaultDeveloperLoginId() { base.SetDefaultDeveloperLoginId(); - var azureOrg = tiles.Where(i => i.AzureUri.IsValid).FirstOrDefault().AzureUri?.Organization ?? string.Empty; + var azureOrg = _tiles.Where(i => i.AzureUri.IsValid).FirstOrDefault().AzureUri?.Organization ?? string.Empty; if (!string.IsNullOrEmpty(azureOrg)) { var devIds = DeveloperIdProvider.GetInstance().GetLoggedInDeveloperIds().DeveloperIds; @@ -392,13 +392,13 @@ protected override void SetDefaultDeveloperLoginId() private void UpdateAllTiles(string data) { var dataObject = JsonObject.Parse(data); - var nTiles = tiles.Count; + var nTiles = _tiles.Count; if (dataObject != null) { for (var i = 0; i < nTiles; ++i) { - var tile = tiles[i]; + var tile = _tiles[i]; var queryFromData = dataObject[$"query{i}"]?.GetValue() ?? string.Empty; var titleFromData = dataObject[$"tileTitle{i}"]?.GetValue() ?? string.Empty; @@ -409,13 +409,13 @@ private void UpdateAllTiles(string data) if (tile.Title != titleFromData) { tile.Title = titleFromData; - tiles[i] = tile; + _tiles[i] = tile; } } else { // The Uri was changed in the ux, we need to recreate the tile with the new Uri. - tiles[i] = new QueryTile(queryFromData, titleFromData); + _tiles[i] = new QueryTile(queryFromData, titleFromData); } } @@ -451,13 +451,13 @@ public override string GetConfiguration(string data) var tilesArray = new JsonArray(); - for (var i = 0; i < tiles.Count; ++i) + for (var i = 0; i < _tiles.Count; ++i) { tilesArray.Add(new JsonObject { - { "url", tiles[i].AzureUri.ToString() }, - { "title", tiles[i].Title }, - { "message", tiles[i].Message.Length != 0 ? tiles[i].Message : null }, + { "url", _tiles[i].AzureUri.ToString() }, + { "title", _tiles[i].Title }, + { "message", _tiles[i].Message.Length != 0 ? _tiles[i].Message : null }, }); } diff --git a/src/AzureExtension/Widgets/AzureWidget.cs b/src/AzureExtension/Widgets/AzureWidget.cs index 3b9ec40b..1bdac853 100644 --- a/src/AzureExtension/Widgets/AzureWidget.cs +++ b/src/AzureExtension/Widgets/AzureWidget.cs @@ -22,7 +22,7 @@ public abstract class AzureWidget : WidgetImpl protected IAzureDataManager? DataManager { get; private set; } - private DateTime lastUpdateRequest = DateTime.MinValue; + private DateTime _lastUpdateRequest = DateTime.MinValue; protected WidgetActivityState ActivityState { get; set; } = WidgetActivityState.Unknown; @@ -394,7 +394,7 @@ protected void SetConfigure() // If moving to configure, reset the throttle so when we update to Active, the first update // will not get throttled. DataUpdater?.Stop(); - lastUpdateRequest = DateTime.MinValue; + _lastUpdateRequest = DateTime.MinValue; ActivityState = WidgetActivityState.Configure; Page = WidgetPageState.Configure; LogCurrentState(); @@ -463,7 +463,7 @@ private async Task PeriodicUpdate() { // Only update per the update interval. // This is intended to be dynamic in the future. - if (DateTime.Now - lastUpdateRequest < WidgetRefreshRate) + if (DateTime.Now - _lastUpdateRequest < WidgetRefreshRate) { return; } @@ -482,7 +482,7 @@ private async Task PeriodicUpdate() Log.Error(ex, "Failed Requesting Update"); } - lastUpdateRequest = DateTime.Now; + _lastUpdateRequest = DateTime.Now; } // This method will attempt to select a DeveloperId if one is not already selected. By default diff --git a/src/AzureExtension/Widgets/WidgetProvider.cs b/src/AzureExtension/Widgets/WidgetProvider.cs index 4f71952b..5f13a4b9 100644 --- a/src/AzureExtension/Widgets/WidgetProvider.cs +++ b/src/AzureExtension/Widgets/WidgetProvider.cs @@ -17,26 +17,26 @@ public sealed class WidgetProvider : IWidgetProvider, IWidgetProvider2 public WidgetProvider() { _log.Debug("Provider Constructed"); - widgetDefinitionRegistry.Add("Azure_QueryList", new WidgetImplFactory()); - widgetDefinitionRegistry.Add("Azure_QueryTiles", new WidgetImplFactory()); - widgetDefinitionRegistry.Add("Azure_PullRequests", new WidgetImplFactory()); + _widgetDefinitionRegistry.Add("Azure_QueryList", new WidgetImplFactory()); + _widgetDefinitionRegistry.Add("Azure_QueryTiles", new WidgetImplFactory()); + _widgetDefinitionRegistry.Add("Azure_PullRequests", new WidgetImplFactory()); RecoverRunningWidgets(); } - private readonly Dictionary widgetDefinitionRegistry = new(); - private readonly Dictionary runningWidgets = new(); + private readonly Dictionary _widgetDefinitionRegistry = new(); + private readonly Dictionary _runningWidgets = new(); private void InitializeWidget(WidgetContext widgetContext, string state) { var widgetId = widgetContext.Id; var widgetDefinitionId = widgetContext.DefinitionId; _log.Debug($"Calling Initialize for Widget Id: {widgetId} - {widgetDefinitionId}"); - if (widgetDefinitionRegistry.TryGetValue(widgetDefinitionId, out var widgetFactory)) + if (_widgetDefinitionRegistry.TryGetValue(widgetDefinitionId, out var widgetFactory)) { - if (!runningWidgets.ContainsKey(widgetId)) + if (!_runningWidgets.ContainsKey(widgetId)) { var widgetImpl = widgetFactory.Create(widgetContext, state); - runningWidgets.Add(widgetId, widgetImpl); + _runningWidgets.Add(widgetId, widgetImpl); } else { @@ -70,7 +70,7 @@ private void RecoverRunningWidgets() foreach (var widgetInfo in runningWidgets) { - if (!this.runningWidgets.ContainsKey(widgetInfo.WidgetContext.Id)) + if (!_runningWidgets.ContainsKey(widgetInfo.WidgetContext.Id)) { InitializeWidget(widgetInfo.WidgetContext, widgetInfo.CustomState); } @@ -89,7 +89,7 @@ public void Activate(WidgetContext widgetContext) { _log.Debug($"Activate id: {widgetContext.Id} definitionId: {widgetContext.DefinitionId}"); var widgetId = widgetContext.Id; - if (runningWidgets.TryGetValue(widgetId, out var widgetImpl)) + if (_runningWidgets.TryGetValue(widgetId, out var widgetImpl)) { widgetImpl.Activate(widgetContext); } @@ -98,7 +98,7 @@ public void Activate(WidgetContext widgetContext) // Called to activate a widget that we don't know about, which is unexpected. Try to recover by creating it. _log.Warning($"Found WidgetId that was not known: {widgetContext.Id}, attempting to recover by creating it."); CreateWidget(widgetContext); - if (runningWidgets.TryGetValue(widgetId, out var widgetImplForUnknownWidget)) + if (_runningWidgets.TryGetValue(widgetId, out var widgetImplForUnknownWidget)) { widgetImplForUnknownWidget.Activate(widgetContext); } @@ -107,7 +107,7 @@ public void Activate(WidgetContext widgetContext) public void Deactivate(string widgetId) { - if (runningWidgets.TryGetValue(widgetId, out var widgetToDeactivate)) + if (_runningWidgets.TryGetValue(widgetId, out var widgetToDeactivate)) { _log.Debug($"Deactivate id: {widgetId}"); widgetToDeactivate.Deactivate(widgetId); @@ -116,11 +116,11 @@ public void Deactivate(string widgetId) public void DeleteWidget(string widgetId, string customState) { - if (runningWidgets.TryGetValue(widgetId, out var widgetToDelete)) + if (_runningWidgets.TryGetValue(widgetId, out var widgetToDelete)) { _log.Information($"DeleteWidget id: {widgetId}"); widgetToDelete.DeleteWidget(widgetId, customState); - runningWidgets.Remove(widgetId); + _runningWidgets.Remove(widgetId); } } @@ -129,7 +129,7 @@ public void OnActionInvoked(WidgetActionInvokedArgs actionInvokedArgs) _log.Debug($"OnActionInvoked id: {actionInvokedArgs.WidgetContext.Id} definitionId: {actionInvokedArgs.WidgetContext.DefinitionId}"); var widgetContext = actionInvokedArgs.WidgetContext; var widgetId = widgetContext.Id; - if (runningWidgets.TryGetValue(widgetId, out var widgetToInvokeAnActionOn)) + if (_runningWidgets.TryGetValue(widgetId, out var widgetToInvokeAnActionOn)) { widgetToInvokeAnActionOn.OnActionInvoked(actionInvokedArgs); } @@ -140,7 +140,7 @@ public void OnCustomizationRequested(WidgetCustomizationRequestedArgs customizat _log.Debug($"OnCustomizationRequested id: {customizationRequestedArgs.WidgetContext.Id} definitionId: {customizationRequestedArgs.WidgetContext.DefinitionId}"); var widgetContext = customizationRequestedArgs.WidgetContext; var widgetId = widgetContext.Id; - if (runningWidgets.TryGetValue(widgetId, out var widgetToCustomize)) + if (_runningWidgets.TryGetValue(widgetId, out var widgetToCustomize)) { widgetToCustomize.OnCustomizationRequested(customizationRequestedArgs); } @@ -151,7 +151,7 @@ public void OnWidgetContextChanged(WidgetContextChangedArgs contextChangedArgs) _log.Debug($"OnWidgetContextChanged id: {contextChangedArgs.WidgetContext.Id} definitionId: {contextChangedArgs.WidgetContext.DefinitionId}"); var widgetContext = contextChangedArgs.WidgetContext; var widgetId = widgetContext.Id; - if (runningWidgets.TryGetValue(widgetId, out var widgetWithAChangedContext)) + if (_runningWidgets.TryGetValue(widgetId, out var widgetWithAChangedContext)) { widgetWithAChangedContext.OnWidgetContextChanged(contextChangedArgs); } diff --git a/src/AzureExtension/Widgets/WidgetServer.cs b/src/AzureExtension/Widgets/WidgetServer.cs index 98b5c2f1..715e9b3a 100644 --- a/src/AzureExtension/Widgets/WidgetServer.cs +++ b/src/AzureExtension/Widgets/WidgetServer.cs @@ -11,7 +11,7 @@ namespace DevHomeAzureExtension.Widgets; public sealed class WidgetServer : IDisposable { - private readonly HashSet registrationCookies = []; + private readonly HashSet _registrationCookies = []; [UnconditionalSuppressMessage( "ReflectionAnalysis", @@ -39,7 +39,7 @@ public void RegisterWidget(Func createWidget) Marshal.ThrowExceptionForHR(hr); } - registrationCookies.Add(cookie); + _registrationCookies.Add(cookie); log.Debug($"Cookie: {cookie}"); hr = Ole32.CoResumeClassObjects(); if (hr < 0) @@ -57,7 +57,7 @@ public void Dispose() { var log = Log.ForContext("SourceContext", nameof(WidgetServer)); log.Debug($"Revoking class object registrations:"); - foreach (var cookie in registrationCookies) + foreach (var cookie in _registrationCookies) { log.Debug($"Cookie: {cookie}"); var hr = Ole32.CoRevokeClassObject(cookie); diff --git a/src/Telemetry/Logger.cs b/src/Telemetry/Logger.cs index 5fe589aa..9eba2d71 100644 --- a/src/Telemetry/Logger.cs +++ b/src/Telemetry/Logger.cs @@ -26,69 +26,69 @@ internal sealed class Logger : ILogger /// private const string ExceptionThrownEventName = "ExceptionThrown"; - private static readonly Guid DefaultRelatedActivityId = Guid.Empty; + private static readonly Guid _defaultRelatedActivityId = Guid.Empty; /// /// Can only have one EventSource alive per process, so just create one statically. /// - private static readonly EventSource TelemetryEventSourceInstance = new TelemetryEventSource(ProviderName); + private static readonly EventSource _telemetryEventSourceInstance = new TelemetryEventSource(ProviderName); /// /// Logs telemetry locally, but shouldn't upload it. Similar to an ETW event. /// Should be the same as EventSourceOptions(), as Verbose is the default level. /// - private static readonly EventSourceOptions LocalOption = new() { Level = EventLevel.Verbose }; + private static readonly EventSourceOptions _localOption = new() { Level = EventLevel.Verbose }; /// /// Logs error telemetry locally, but shouldn't upload it. Similar to an ETW event. /// - private static readonly EventSourceOptions LocalErrorOption = new() { Level = EventLevel.Error }; + private static readonly EventSourceOptions _localErrorOption = new() { Level = EventLevel.Error }; /// /// Logs telemetry. /// Currently this is at 0% sampling for both internal and external retail devices. /// - private static readonly EventSourceOptions InfoOption = new() { Keywords = TelemetryEventSource.TelemetryKeyword }; + private static readonly EventSourceOptions _infoOption = new() { Keywords = TelemetryEventSource.TelemetryKeyword }; /// /// Logs error telemetry. /// Currently this is at 0% sampling for both internal and external retail devices. /// - private static readonly EventSourceOptions InfoErrorOption = new() { Level = EventLevel.Error, Keywords = TelemetryEventSource.TelemetryKeyword }; + private static readonly EventSourceOptions _infoErrorOption = new() { Level = EventLevel.Error, Keywords = TelemetryEventSource.TelemetryKeyword }; /// /// Logs measure telemetry. /// This should be sent back on internal devices, and a small, sampled % of external retail devices. /// - private static readonly EventSourceOptions MeasureOption = new() { Keywords = TelemetryEventSource.MeasuresKeyword }; + private static readonly EventSourceOptions _measureOption = new() { Keywords = TelemetryEventSource.MeasuresKeyword }; /// /// Logs measure error telemetry. /// This should be sent back on internal devices, and a small, sampled % of external retail devices. /// - private static readonly EventSourceOptions MeasureErrorOption = new() { Level = EventLevel.Error, Keywords = TelemetryEventSource.MeasuresKeyword }; + private static readonly EventSourceOptions _measureErrorOption = new() { Level = EventLevel.Error, Keywords = TelemetryEventSource.MeasuresKeyword }; /// /// Logs critical telemetry. /// This should be sent back on all devices sampled at 100%. /// - private static readonly EventSourceOptions CriticalDataOption = new() { Keywords = TelemetryEventSource.CriticalDataKeyword }; + private static readonly EventSourceOptions _criticalDataOption = new() { Keywords = TelemetryEventSource.CriticalDataKeyword }; /// /// Logs critical error telemetry. /// This should be sent back on all devices sampled at 100%. /// - private static readonly EventSourceOptions CriticalDataErrorOption = new() { Level = EventLevel.Error, Keywords = TelemetryEventSource.CriticalDataKeyword }; + private static readonly EventSourceOptions _criticalDataErrorOption = new() { Level = EventLevel.Error, Keywords = TelemetryEventSource.CriticalDataKeyword }; /// /// ActivityId so we can correlate all events in the same run /// - private static Guid activityId = Guid.NewGuid(); + private static Guid _activityId = Guid.NewGuid(); /// /// List of strings we should try removing for sensitivity reasons. /// - private readonly List> sensitiveStrings = new(); + private readonly List> _sensitiveStrings = new(); /// /// Initializes a new instance of the class. @@ -116,9 +116,9 @@ public void AddSensitiveString(string name, string replaceWith) { // Make sure the name isn't blank, hasn't already been added, and is greater than three characters. // Otherwise they could name their VM "a", and then we would end up replacing every "a" with another string. - if (!string.IsNullOrWhiteSpace(name) && name.Length > 3 && !sensitiveStrings.Exists(item => name.Equals(item.Key, StringComparison.Ordinal))) + if (!string.IsNullOrWhiteSpace(name) && name.Length > 3 && !_sensitiveStrings.Exists(item => name.Equals(item.Key, StringComparison.Ordinal))) { - sensitiveStrings.Add(new KeyValuePair(name, replaceWith ?? string.Empty)); + _sensitiveStrings.Add(new KeyValuePair(name, replaceWith ?? string.Empty)); } } @@ -156,7 +156,7 @@ public void LogException(string action, Exception e, Guid? relatedActivityId = n innerStackTrace = innerStackTrace.ToString(), message = ReplaceSensitiveStrings(e.Message), }, - relatedActivityId ?? DefaultRelatedActivityId); + relatedActivityId ?? _defaultRelatedActivityId); } /// @@ -175,7 +175,7 @@ public void LogTimeTaken(string eventName, uint timeTakenMilliseconds, Guid? rel eventName, timeTakenMilliseconds, }, - relatedActivityId ?? DefaultRelatedActivityId); + relatedActivityId ?? _defaultRelatedActivityId); } /// @@ -188,7 +188,7 @@ public void LogTimeTaken(string eventName, uint timeTakenMilliseconds, Guid? rel /// Anonymous type. public void Log<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(string eventName, LogLevel level, T data, Guid? relatedActivityId = null) { - WriteTelemetryEvent(eventName, level, relatedActivityId ?? DefaultRelatedActivityId, false, data); + WriteTelemetryEvent(eventName, level, relatedActivityId ?? _defaultRelatedActivityId, false, data); } /// @@ -201,7 +201,7 @@ public void LogTimeTaken(string eventName, uint timeTakenMilliseconds, Guid? rel /// Anonymous type. public void LogError<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(string eventName, LogLevel level, T data, Guid? relatedActivityId = null) { - WriteTelemetryEvent(eventName, level, relatedActivityId ?? DefaultRelatedActivityId, true, data); + WriteTelemetryEvent(eventName, level, relatedActivityId ?? _defaultRelatedActivityId, true, data); } /// @@ -213,7 +213,7 @@ private string ReplaceSensitiveStrings(string message) { if (message != null) { - foreach (var pair in sensitiveStrings) + foreach (var pair in _sensitiveStrings) { // There's no String.Replace() with case insensitivity. // We could use Regular Expressions here for searching for case-insensitive string matches, @@ -263,19 +263,19 @@ private string ReplaceSensitiveStrings(string message) { telemetryOptions = level switch { - LogLevel.Critical => isError ? Logger.CriticalDataErrorOption : Logger.CriticalDataOption, - LogLevel.Measure => isError ? Logger.MeasureErrorOption : Logger.MeasureOption, - LogLevel.Info => isError ? Logger.InfoErrorOption : Logger.InfoOption, - _ => isError ? Logger.LocalErrorOption : Logger.LocalOption, + LogLevel.Critical => isError ? Logger._criticalDataErrorOption : Logger._criticalDataOption, + LogLevel.Measure => isError ? Logger._measureErrorOption : Logger._measureOption, + LogLevel.Info => isError ? Logger._infoErrorOption : Logger._infoOption, + _ => isError ? Logger._localErrorOption : Logger._localOption, }; } else { // The telemetry is not turned on, downgrade to local telemetry - telemetryOptions = isError ? Logger.LocalErrorOption : Logger.LocalOption; + telemetryOptions = isError ? Logger._localErrorOption : Logger._localOption; } - TelemetryEventSourceInstance.Write(eventName, ref telemetryOptions, ref activityId, ref relatedActivityId, ref data); + _telemetryEventSourceInstance.Write(eventName, ref telemetryOptions, ref _activityId, ref relatedActivityId, ref data); } internal void AddWellKnownSensitiveStrings() diff --git a/src/Telemetry/LoggerFactory.cs b/src/Telemetry/LoggerFactory.cs index f6b32a85..623a51b3 100644 --- a/src/Telemetry/LoggerFactory.cs +++ b/src/Telemetry/LoggerFactory.cs @@ -8,22 +8,22 @@ namespace DevHomeAzureExtension.Telemetry; /// This would be useful for future when we have updated interfaces for logger like ILogger2, ILogger3 and so on public class LoggerFactory { - private static readonly object LockObj = new(); + private static readonly object _lockObj = new(); - private static Logger loggerInstance; + private static Logger _loggerInstance; private static Logger GetLoggerInstance() { - if (loggerInstance == null) + if (_loggerInstance == null) { - lock (LockObj) + lock (_lockObj) { - loggerInstance ??= new Logger(); - loggerInstance.AddWellKnownSensitiveStrings(); + _loggerInstance ??= new Logger(); + _loggerInstance.AddWellKnownSensitiveStrings(); } } - return loggerInstance; + return _loggerInstance; } /// diff --git a/test/AzureExtension/Widgets/Icons.cs b/test/AzureExtension/Widgets/Icons.cs index 87bbace9..bc5f6e51 100644 --- a/test/AzureExtension/Widgets/Icons.cs +++ b/test/AzureExtension/Widgets/Icons.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using DevHome.Logging; - namespace DevHomeAzureExtension.Test; public partial class WidgetTests diff --git a/test/AzureExtension/Widgets/Validation.cs b/test/AzureExtension/Widgets/Validation.cs index f835b603..4bd74031 100644 --- a/test/AzureExtension/Widgets/Validation.cs +++ b/test/AzureExtension/Widgets/Validation.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using DevHome.Logging; using DevHomeAzureExtension.Client; namespace DevHomeAzureExtension.Test; diff --git a/test/AzureExtension/Widgets/WidgetTestsSetup.cs b/test/AzureExtension/Widgets/WidgetTestsSetup.cs index 17d2c62b..ba90df0c 100644 --- a/test/AzureExtension/Widgets/WidgetTestsSetup.cs +++ b/test/AzureExtension/Widgets/WidgetTestsSetup.cs @@ -12,12 +12,12 @@ public TestContext? TestContext set; } - private TestOptions testOptions = new(); + private TestOptions _testOptions = new(); private TestOptions TestOptions { - get => testOptions; - set => testOptions = value; + get => _testOptions; + set => _testOptions = value; } [TestInitialize] From 52754a6e8c4ea81bbd7f8a507fe235fc7f6a4c12 Mon Sep 17 00:00:00 2001 From: Kristen Schau <47155823+krschau@users.noreply.github.com> Date: Thu, 20 Jun 2024 19:56:08 +0100 Subject: [PATCH 02/11] Follow naming rules for DevBox, Quickstart, DevId, Repo code (#224) --- src/AzureExtension/Client/AzureUri.cs | 4 +- .../Contracts/IDevBoxOperationWatcher.cs | 2 +- src/AzureExtension/DevBox/Constants.cs | 16 +-- src/AzureExtension/DevBox/DevBoxInstance.cs | 33 +++---- src/AzureExtension/DevBox/DevBoxProvider.cs | 24 +++-- .../DevBox/Helpers/DevBoxOperationHelper.cs | 4 - .../DevBox/Helpers/TaskJSONToCSClasses.cs | 1 + .../DevBox/Helpers/TaskYAMLToCSClasses.cs | 3 +- .../WaitingForUserAdaptiveCardSession.cs | 99 +++++++++---------- .../DevBox/Helpers/WingetConfigWrapper.cs | 48 ++++----- .../Models/DevBoxOperationResponseHeader.cs | 2 +- .../DeveloperId/AuthenticationSettings.cs | 10 +- .../DeveloperId/DeveloperIdProvider.cs | 30 +++--- .../Helpers/AzureRepositoryHierarchy.cs | 4 +- src/AzureExtension/Helpers/IconLoader.cs | 6 +- .../Notifications/NotificationManager.cs | 8 +- .../Providers/DevHomeRepository.cs | 12 +-- .../Providers/RepositoryProvider.cs | 60 +++++------ .../Providers/SettingsUIController.cs | 6 +- .../DependencyUIController.cs | 34 +++---- .../DevContainerGenerationOperation.cs | 4 +- .../QuickStartPlayground/EmbeddingsCalc.cs | 14 +-- .../ExtensionInitializationUIController.cs | 6 +- .../InstalledAppsService.cs | 4 +- ...AIDevContainerQuickStartProjectProvider.cs | 10 +- .../Services/DevBox/ARMTokenService.cs | 2 +- .../Services/DevBox/DataTokenService.cs | 2 +- .../Services/DevBox/DevBoxCreationManager.cs | 2 - .../Services/DevBox/DevBoxOperationWatcher.cs | 44 ++++----- .../Services/DevBox/PackagesService.cs | 5 +- .../Services/DevBox/TimeSpanService.cs | 4 +- .../Strings/en-US/Resources.resw | 2 +- .../DataStore/DataStoreTestsSetup.cs | 6 +- test/AzureExtension/DevBox/DevBoxTests.cs | 4 - .../AzureExtension/DevBox/DevBoxTestsSetup.cs | 27 +++-- .../DevBox/TimeSpanServiceMock.cs | 5 - .../DeveloperId/DeveloperIdTests.cs | 2 +- .../DeveloperId/DeveloperIdTestsSetup.cs | 12 +-- .../Mocks/MockAuthenticationHelper.cs | 10 +- .../Helpers/TestSetupHelpers.cs | 4 +- .../RepositoryProviderTests.cs | 1 - .../RepositoryProviderTestsSetup.cs | 6 +- 42 files changed, 277 insertions(+), 305 deletions(-) diff --git a/src/AzureExtension/Client/AzureUri.cs b/src/AzureExtension/Client/AzureUri.cs index dee490b2..7be4f92e 100644 --- a/src/AzureExtension/Client/AzureUri.cs +++ b/src/AzureExtension/Client/AzureUri.cs @@ -14,7 +14,7 @@ namespace DevHomeAzureExtension.Client; // using. Instead you just check if it's valid and can then use the object and trust the result. public class AzureUri { - private static readonly string ValidUriString = "https://www.microsoft.com/"; + private const string ValidUriString = "https://www.microsoft.com/"; private readonly bool _validUri; @@ -491,7 +491,7 @@ private Uri InitializeConnection() break; case AzureHostType.NotHosted: - // Onprem includes the collection. + // OnPrem includes the collection. var onpremUriString = Uri.Scheme + "://" + Uri.Authority; onpremUriString = onpremUriString.TrimEnd('/') + '/'; if (!Uri.TryCreate(onpremUriString, UriKind.Absolute, out newUri)) diff --git a/src/AzureExtension/Contracts/IDevBoxOperationWatcher.cs b/src/AzureExtension/Contracts/IDevBoxOperationWatcher.cs index fd50299a..fa8af34d 100644 --- a/src/AzureExtension/Contracts/IDevBoxOperationWatcher.cs +++ b/src/AzureExtension/Contracts/IDevBoxOperationWatcher.cs @@ -8,7 +8,7 @@ namespace AzureExtension.Contracts; /// -/// Inteface used to watch Dev Box operations asynchronously that take place in the Dev Center. +/// Interface used to watch Dev Box operations asynchronously that take place in the Dev Center. /// public interface IDevBoxOperationWatcher { diff --git a/src/AzureExtension/DevBox/Constants.cs b/src/AzureExtension/DevBox/Constants.cs index a713ec2b..1bd607fd 100644 --- a/src/AzureExtension/DevBox/Constants.cs +++ b/src/AzureExtension/DevBox/Constants.cs @@ -27,15 +27,15 @@ public static class Constants /// /// API version used for enumeration and start, stop, and restart APIs /// - /// For stable api's - /// for preview api's + /// For stable APIs + /// for preview APIs public const string APIVersion = "api-version=2024-05-01-preview"; /// /// DevCenter API to get all devboxes /// - /// for stable api's - /// for preview api's + /// for stable APIs + /// for preview APIs public const string DevBoxAPI = "/users/me/devboxes?" + APIVersion; public const string DevBoxUserSegmentOfUri = "/users/me/devboxes"; @@ -179,7 +179,7 @@ public static class DevBoxPowerStates /// Icons should be located in an extensions resource.pri file which is generated at build time. /// See the MakePri.exe documentation for how you can view what is in the resource.pri file, so you can find the location of your icon. /// https://learn.microsoft.com/en-us/windows/uwp/app-resources/makepri-exe-command-options. (use MakePri.exe in a VS Developer Command Prompt or - /// Powershell window) + /// PowerShell window) /// #if CANARY_BUILD public const string ProviderIcon = "ms-resource://Microsoft.Windows.DevHomeAzureExtension.Canary/Files/AzureExtension/Assets/DevBoxProvider.png"; @@ -227,9 +227,9 @@ public static class DevBoxPowerStates public const string DevBoxCheckLogsKey = "DevBox_CheckLogs"; /// - /// Resource key for the error message when Dev Boxes retrival failed. + /// Resource key for the error message when Dev Boxes retrieval failed. /// - public const string RetrivalFailKey = "DevBox_RetrivalFailKey"; + public const string RetrievalFailKey = "DevBox_RetrievalFailKey"; /// /// Resource key for the error message when there is no default user logged in. @@ -237,7 +237,7 @@ public static class DevBoxPowerStates public const string NoDefaultUserFailKey = "DevBox_NoDefaultUserFailKey"; /// - /// Resource key for the error message when Dev Boxes retrival failed. + /// Resource key for the error message when Dev Boxes retrieval failed. /// public const string SessionExpiredKey = "DevBox_SessionExpired"; diff --git a/src/AzureExtension/DevBox/DevBoxInstance.cs b/src/AzureExtension/DevBox/DevBoxInstance.cs index 0df0cf1b..98e665b4 100644 --- a/src/AzureExtension/DevBox/DevBoxInstance.cs +++ b/src/AzureExtension/DevBox/DevBoxInstance.cs @@ -1,14 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System; using System.Diagnostics; using System.Globalization; using System.Runtime.InteropServices; -using System.Runtime.InteropServices.WindowsRuntime; using System.Text; using System.Text.Json; -using System.Threading; using System.Web; using AzureExtension.Contracts; using AzureExtension.DevBox.DevBoxJsonToCsClasses; @@ -21,10 +18,6 @@ using Windows.ApplicationModel; using Windows.Foundation; -using Windows.Storage; -using Windows.Storage.Streams; -using Windows.Win32; - namespace AzureExtension.DevBox; public delegate DevBoxInstance DevBoxInstanceFactory(IDeveloperId developerId, DevBoxMachineState devBoxJson); @@ -72,10 +65,10 @@ public class DevBoxInstance : IComputeSystem, IComputeSystem2 private const string DevBoxMultipleConcurrentOperationsNotSupportedKey = "DevBox_MultipleConcurrentOperationsNotSupport"; - private static readonly CompositeFormat ProtocolPinString = CompositeFormat.Parse("ms-cloudpc:pin?location={0}&request={1}&cpcid={2}&workspaceName={3}&environment={4}&username={5}&version=0.0&source=DevHome"); + private static readonly CompositeFormat _protocolPinString = CompositeFormat.Parse("ms-cloudpc:pin?location={0}&request={1}&cpcid={2}&workspaceName={3}&environment={4}&username={5}&version=0.0&source=DevHome"); // This is the version of the Windows App package that supports protocol associations for pinning - private static readonly PackageVersion MinimumWindowsAppVersion = new(1, 3, 243, 0); + private static readonly PackageVersion _minimumWindowsAppVersion = new(1, 3, 243, 0); // These exit codes must be kept in sync with WindowsApp private const int ExitCodeInvalid = -1; @@ -243,7 +236,7 @@ private ComputeSystemOperations GetOperations() if (_packagesService.IsPackageInstalled(Constants.WindowsAppPackageFamilyName)) { PackageVersion version = _packagesService.GetPackageInstalledVersion(Constants.WindowsAppPackageFamilyName); - if (IsPackageVersionGreaterThan(version, MinimumWindowsAppVersion)) + if (IsPackageVersionGreaterThan(version, _minimumWindowsAppVersion)) { operations |= ComputeSystemOperations.PinToStartMenu | ComputeSystemOperations.PinToTaskbar; } @@ -351,7 +344,7 @@ public void ProvisioningMonitorCompleted(DevBoxMachineState? devBoxMachineState, { _log.Information($"Dev Box provisioning failed for '{DisplayName}'"); - // If the provisioning failed, we'll set the state to failed and powerstate to unknown. + // If the provisioning failed, we'll set the state to failed and power state to unknown. // The PowerState being unknown will make the UI show the Dev Box state as unknown. DevBoxState.ProvisioningState = Constants.DevBoxProvisioningStates.Failed; DevBoxState.PowerState = Constants.DevBoxPowerStates.Unknown; @@ -699,7 +692,7 @@ private string ValidateWindowsAppAndItsParameters() PackageVersion version = _packagesService.GetPackageInstalledVersion(Constants.WindowsAppPackageFamilyName); - if (!IsPackageVersionGreaterThan(version, MinimumWindowsAppVersion)) + if (!IsPackageVersionGreaterThan(version, _minimumWindowsAppVersion)) { return "Older version of Windows App installed on the system"; } @@ -726,10 +719,10 @@ public IAsyncOperation DoPinActionAsync(string loc throw new InvalidDataException(errorString); } - var exitcode = ExitCodeInvalid; + var exitCode = ExitCodeInvalid; var psi = new ProcessStartInfo(); psi.UseShellExecute = true; - psi.FileName = string.Format(CultureInfo.InvariantCulture, ProtocolPinString, location, pinAction, WorkspaceId, DisplayName, Environment, Username); + psi.FileName = string.Format(CultureInfo.InvariantCulture, _protocolPinString, location, pinAction, WorkspaceId, DisplayName, Environment, Username); Process? process = Process.Start(psi); if (process != null) @@ -738,19 +731,19 @@ public IAsyncOperation DoPinActionAsync(string loc AllowSetForegroundWindow(process.Id); // This signals to the WindowsApp that it has been given foreground rights - EventWaitHandle signalForegroundSet = new EventWaitHandle(false, EventResetMode.AutoReset, WindowsAppEventName); + var signalForegroundSet = new EventWaitHandle(false, EventResetMode.AutoReset, WindowsAppEventName); signalForegroundSet.Set(); process.WaitForExit(); - exitcode = process.ExitCode; - if (exitcode == ExitCodeSuccess) + exitCode = process.ExitCode; + if (exitCode == ExitCodeSuccess) { UpdateStateForUI(); return new ComputeSystemOperationResult(); } } - errorString = $"DoPinActionAsync with location {location} and action {pinAction} failed with exitcode: {exitcode}"; + errorString = $"DoPinActionAsync with location {location} and action {pinAction} failed with exitCode: {exitCode}"; throw new NotSupportedException(errorString); } catch (Exception ex) @@ -805,7 +798,7 @@ public IAsyncOperation GetPinStatusAsync(string locat var exitcode = ExitCodeInvalid; var psi = new ProcessStartInfo(); psi.UseShellExecute = true; - psi.FileName = string.Format(CultureInfo.InvariantCulture, ProtocolPinString, location, "status", WorkspaceId, DisplayName, Environment, Username); + psi.FileName = string.Format(CultureInfo.InvariantCulture, _protocolPinString, location, "status", WorkspaceId, DisplayName, Environment, Username); Process? process = Process.Start(psi); if (process != null) { @@ -813,7 +806,7 @@ public IAsyncOperation GetPinStatusAsync(string locat AllowSetForegroundWindow(process.Id); // This signals to the WindowsApp that it has been given foreground rights - EventWaitHandle signalForegroundSet = new EventWaitHandle(false, EventResetMode.AutoReset, WindowsAppEventName); + var signalForegroundSet = new EventWaitHandle(false, EventResetMode.AutoReset, WindowsAppEventName); signalForegroundSet.Set(); process.WaitForExit(); diff --git a/src/AzureExtension/DevBox/DevBoxProvider.cs b/src/AzureExtension/DevBox/DevBoxProvider.cs index b1579e74..9d64bc78 100644 --- a/src/AzureExtension/DevBox/DevBoxProvider.cs +++ b/src/AzureExtension/DevBox/DevBoxProvider.cs @@ -2,8 +2,6 @@ // Licensed under the MIT License. using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Text; using System.Text.Json; using AzureExtension.Contracts; @@ -37,7 +35,7 @@ public class DevBoxProvider : IComputeSystemProvider private readonly Dictionary> _cachedDevBoxesMap = new(); - private ConcurrentBag devBoxes = new(); + private readonly ConcurrentBag _devBoxes = new(); public DevBoxProvider( IDevBoxManagementService mgmtSvc, @@ -85,7 +83,7 @@ private async Task ProcessAllDevBoxesInProjectAsync(DevBoxProject devBoxProject, // If the Dev Box's creation operation or its provisioning state are being tracked by us, then don't make a new instance. // Add the one we're tracking. This is to avoid adding the same Dev Box twice. E.g User clicks Dev Homes refresh button while // the Dev Box is being created. - devBoxes.Add(devBox!); + _devBoxes.Add(devBox!); continue; } @@ -100,12 +98,12 @@ private async Task ProcessAllDevBoxesInProjectAsync(DevBoxProject devBoxProject, // It was likely created by Dev Portals UI or some other non-Dev Home related UI. _devBoxCreationManager.StartDevBoxProvisioningStateMonitor(newDevBoxInstance.AssociatedDeveloperId, newDevBoxInstance); } - else - { - await newDevBoxInstance.LoadWindowsAppParameters(); + else + { + await newDevBoxInstance.LoadWindowsAppParameters(); } - devBoxes.Add(newDevBoxInstance); + _devBoxes.Add(newDevBoxInstance); } } } @@ -121,7 +119,7 @@ public async Task> GetDevBoxesAsync(IDeveloperId dev if (devBoxProjects?.Data != null) { _log.Information($"Found {devBoxProjects.Data.Length} projects"); - devBoxes.Clear(); + _devBoxes.Clear(); var cancellationTokenSource = new CancellationTokenSource(); cancellationTokenSource.CancelAfter(TimeSpan.FromMinutes(2)); var token = cancellationTokenSource.Token; @@ -142,7 +140,7 @@ await Parallel.ForEachAsync(devBoxProjects.Data, async (project, token) => // update the cache every time we retrieve new Dev Boxes. This is used so in the creation flow we don't need // to retrieve the Dev Boxes again if the user already retrieved them in the environments page in Dev Home var uniqueUserId = GetUniqueDeveloperId(developerId); - _cachedDevBoxesMap[uniqueUserId] = devBoxes.ToList(); + _cachedDevBoxesMap[uniqueUserId] = _devBoxes.ToList(); return _cachedDevBoxesMap[uniqueUserId]; } @@ -172,11 +170,11 @@ public IAsyncOperation GetComputeSystemsAsync(IDeveloperId var errorMessage = Constants.OperationsDefaultErrorMsg; if (ex.InnerException != null && ex.InnerException.Message.Contains("Account has previously been signed out of this application")) { - errorMessage = Resources.GetResource(Constants.RetrivalFailKey, developerId.LoginId) + Resources.GetResource(Constants.SessionExpiredKey); + errorMessage = Resources.GetResource(Constants.RetrievalFailKey, developerId.LoginId) + Resources.GetResource(Constants.SessionExpiredKey); } else if (ex.Message.Contains("A passthrough token was detected without proper resource provider context")) { - errorMessage = Resources.GetResource(Constants.RetrivalFailKey, developerId.LoginId) + Resources.GetResource(Constants.UnconfiguredKey); + errorMessage = Resources.GetResource(Constants.RetrievalFailKey, developerId.LoginId) + Resources.GetResource(Constants.UnconfiguredKey); } else if (ex is ArgumentException ae) { @@ -184,7 +182,7 @@ public IAsyncOperation GetComputeSystemsAsync(IDeveloperId } else { - errorMessage = Resources.GetResource(Constants.RetrivalFailKey, developerId.LoginId) + ex.Message; + errorMessage = Resources.GetResource(Constants.RetrievalFailKey, developerId.LoginId) + ex.Message; } _log.Error(ex, errorMessage); diff --git a/src/AzureExtension/DevBox/Helpers/DevBoxOperationHelper.cs b/src/AzureExtension/DevBox/Helpers/DevBoxOperationHelper.cs index cc1baaec..3923376d 100644 --- a/src/AzureExtension/DevBox/Helpers/DevBoxOperationHelper.cs +++ b/src/AzureExtension/DevBox/Helpers/DevBoxOperationHelper.cs @@ -1,11 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.Text; -using AzureExtension.DevBox.Models; using Microsoft.Windows.DevHome.SDK; -using YamlDotNet.Serialization; -using YamlDotNet.Serialization.NamingConventions; namespace AzureExtension.DevBox.Helpers; diff --git a/src/AzureExtension/DevBox/Helpers/TaskJSONToCSClasses.cs b/src/AzureExtension/DevBox/Helpers/TaskJSONToCSClasses.cs index 9fe05c37..ad3e7445 100644 --- a/src/AzureExtension/DevBox/Helpers/TaskJSONToCSClasses.cs +++ b/src/AzureExtension/DevBox/Helpers/TaskJSONToCSClasses.cs @@ -37,6 +37,7 @@ namespace AzureExtension.DevBox.Helpers; // "startTime": "2024-04-09T20:09:03.0136738+00:00" // } // + /// /// Represents the classes for the customization task JSON response. /// diff --git a/src/AzureExtension/DevBox/Helpers/TaskYAMLToCSClasses.cs b/src/AzureExtension/DevBox/Helpers/TaskYAMLToCSClasses.cs index 65e344f2..9e94b924 100644 --- a/src/AzureExtension/DevBox/Helpers/TaskYAMLToCSClasses.cs +++ b/src/AzureExtension/DevBox/Helpers/TaskYAMLToCSClasses.cs @@ -35,6 +35,7 @@ namespace AzureExtension.DevBox.Helpers; // - 'Git.Git | Install: Git' // configurationVersion: 0.2.0 // + /// /// Represents the classes for the YAML customization task. /// @@ -71,7 +72,7 @@ public class Directives { public string Description { get; set; } = string.Empty; - public bool? AllowPrerelease { get; set; } + public bool? AllowPrerelease { get; set; } public string SecurityContext { get; set; } = "current"; } diff --git a/src/AzureExtension/DevBox/Helpers/WaitingForUserAdaptiveCardSession.cs b/src/AzureExtension/DevBox/Helpers/WaitingForUserAdaptiveCardSession.cs index b8781c71..fdbca1c1 100644 --- a/src/AzureExtension/DevBox/Helpers/WaitingForUserAdaptiveCardSession.cs +++ b/src/AzureExtension/DevBox/Helpers/WaitingForUserAdaptiveCardSession.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.Drawing; using System.Text; using System.Text.Json; using System.Text.Json.Nodes; @@ -13,32 +12,32 @@ namespace AzureExtension.DevBox.Helpers; public class WaitingForUserAdaptiveCardSession : IExtensionAdaptiveCardSession2, IDisposable -{ +{ + private readonly ManualResetEvent _resumeEvent; + + private readonly ManualResetEvent _launchEvent; + private IExtensionAdaptiveCard? _extensionAdaptiveCard; private string? _template; - private ManualResetEvent _resumeEvent; - - private ManualResetEvent _launchEvent; - - private const string _waitingForUserSessionState = "WaitingForUserSession"; - - private const string _launchAction = "launchAction"; - - private const string _resumeAction = "resumeAction"; - - private readonly ILogger _log = Log.ForContext("SourceContext", nameof(WaitingForUserAdaptiveCardSession)); - - private JsonSerializerOptions _serializerOptions = new() - { - PropertyNameCaseInsensitive = true, - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + private const string WaitingForUserSessionState = "WaitingForUserSession"; + + private const string LaunchAction = "launchAction"; + + private const string ResumeAction = "resumeAction"; + + private readonly ILogger _log = Log.ForContext("SourceContext", nameof(WaitingForUserAdaptiveCardSession)); + + private readonly JsonSerializerOptions _serializerOptions = new() + { + PropertyNameCaseInsensitive = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, }; public WaitingForUserAdaptiveCardSession(ManualResetEvent resumeEvent, ManualResetEvent launchEvent) { - _resumeEvent = resumeEvent; + _resumeEvent = resumeEvent; _launchEvent = launchEvent; } @@ -58,7 +57,7 @@ public ProviderOperationResult Initialize(IExtensionAdaptiveCard extensionUI) { "icon", ConvertIconToDataString("Caution.png") }, { "loginRequiredText", Resources.GetResource("DevBox_AdaptiveCard_Text") }, { "loginRequiredDescriptionText", Resources.GetResource("DevBox_AdaptiveCard_InnerDescription") }, - { "LaunchText", Resources.GetResource("DevBox_AdaptiveCard_LaunchText") }, + { "LaunchText", Resources.GetResource("DevBox_AdaptiveCard_LaunchText") }, { "ResumeText", Resources.GetResource("DevBox_AdaptiveCard_ResumeText") }, }; @@ -90,42 +89,42 @@ private static string ConvertIconToDataString(string fileName) public IAsyncOperation OnAction(string action, string inputs) { return Task.Run(() => - { - try - { + { + try + { var state = _extensionAdaptiveCard?.State; - ProviderOperationResult operationResult; + ProviderOperationResult operationResult; var data = JsonSerializer.Deserialize(action, _serializerOptions); - if (state != null && state.Equals(_waitingForUserSessionState, StringComparison.OrdinalIgnoreCase) && data != null) - { - switch (data.Id) - { - case _launchAction: - operationResult = new ProviderOperationResult(ProviderOperationStatus.Success, null, null, null); - _launchEvent.Set(); - _resumeEvent.Set(); - break; - case _resumeAction: - operationResult = new ProviderOperationResult(ProviderOperationStatus.Success, null, null, null); - _resumeEvent.Set(); - break; - default: - throw new InvalidOperationException($"Unexpected action:{data.Id}"); - } - - Stopped.Invoke(this, new(operationResult, string.Empty)); + if (state != null && state.Equals(WaitingForUserSessionState, StringComparison.OrdinalIgnoreCase) && data != null) + { + switch (data.Id) + { + case LaunchAction: + operationResult = new ProviderOperationResult(ProviderOperationStatus.Success, null, null, null); + _launchEvent.Set(); + _resumeEvent.Set(); + break; + case ResumeAction: + operationResult = new ProviderOperationResult(ProviderOperationStatus.Success, null, null, null); + _resumeEvent.Set(); + break; + default: + throw new InvalidOperationException($"Unexpected action:{data.Id}"); + } + + Stopped.Invoke(this, new(operationResult, string.Empty)); } else - { + { throw new InvalidOperationException($"Unexpected state:{state} or Parsing Error"); - } - + } + return operationResult; - } - catch (Exception ex) - { - _log.Error(ex, "Error occurred while processing the action"); - return new ProviderOperationResult(ProviderOperationStatus.Failure, null, ex.Message, ex.StackTrace); + } + catch (Exception ex) + { + _log.Error(ex, "Error occurred while processing the action"); + return new ProviderOperationResult(ProviderOperationStatus.Failure, null, ex.Message, ex.StackTrace); } }).AsAsyncOperation(); } diff --git a/src/AzureExtension/DevBox/Helpers/WingetConfigWrapper.cs b/src/AzureExtension/DevBox/Helpers/WingetConfigWrapper.cs index aa72912f..5fe286fb 100644 --- a/src/AzureExtension/DevBox/Helpers/WingetConfigWrapper.cs +++ b/src/AzureExtension/DevBox/Helpers/WingetConfigWrapper.cs @@ -50,48 +50,48 @@ public class WingetConfigWrapper : IApplyConfigurationOperation, IDisposable public event TypedEventHandler ConfigurationSetStateChanged = (s, e) => { }; - private JsonSerializerOptions _taskJsonSerializerOptions = new() + private readonly JsonSerializerOptions _taskJsonSerializerOptions = new() { PropertyNameCaseInsensitive = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase, }; - private string _fullTaskJSON = string.Empty; + private readonly string _taskAPI; - private List _units = new(); + private readonly string _baseAPI; - private OpenConfigurationSetResult _openConfigurationSetResult = new(null, null, null, 0, 0); + private readonly IDevBoxManagementService _managementService; - private ApplyConfigurationSetResult _applyConfigurationSetResult = new(null, null); + private readonly IDeveloperId _devId; - private string _taskAPI; + private readonly Serilog.ILogger _log; - private string _baseAPI; + private readonly ManualResetEvent _launchEvent = new(false); - private IDevBoxManagementService _managementService; + private readonly ManualResetEvent _resumeEvent = new(false); - private IDeveloperId _devId; + private readonly ComputeSystemState _computeSystemState; - private Serilog.ILogger _log; + private readonly Func> _connectAsync; - private string[] _oldUnitState = Array.Empty(); + // Using a common failure result for all the tasks + // since we don't get any other information from the REST API + private readonly ConfigurationUnitResultInformation _commonFailureResult = new( + new WingetConfigurationException("Runtime Failure"), string.Empty, string.Empty, ConfigurationUnitResultSource.UnitProcessing); - private bool _pendingNotificationShown; + private string _fullTaskJSON = string.Empty; - private ManualResetEvent _launchEvent = new(false); + private List _units = new(); - private ManualResetEvent _resumeEvent = new(false); + private OpenConfigurationSetResult _openConfigurationSetResult = new(null, null, null, 0, 0); - private ComputeSystemState _computeSystemState; + private ApplyConfigurationSetResult _applyConfigurationSetResult = new(null, null); - private Func> _connectAsync; + private string[] _oldUnitState = Array.Empty(); - private bool _alreadyUpdatedUI; + private bool _pendingNotificationShown; - // Using a common failure result for all the tasks - // since we don't get any other information from the REST API - private ConfigurationUnitResultInformation _commonfailureResult = new ConfigurationUnitResultInformation( - new WingetConfigurationException("Runtime Failure"), string.Empty, string.Empty, ConfigurationUnitResultSource.UnitProcessing); + private bool _alreadyUpdatedUI; public WingetConfigWrapper( string configuration, @@ -190,7 +190,7 @@ private void HandleEndState(TaskJSONToCSClasses.BaseClass response, bool isSetSu } else { - unitResults.Add(new(task, ConfigurationUnitState.Completed, false, false, _commonfailureResult)); + unitResults.Add(new(task, ConfigurationUnitState.Completed, false, false, _commonFailureResult)); } } @@ -216,8 +216,8 @@ private void SetStateForCustomizationTask(TaskJSONToCSClasses.BaseClass response break; case "Running": - bool isAnyTaskRunning = false; - bool isWaitingForUserSession = false; + var isAnyTaskRunning = false; + var isWaitingForUserSession = false; for (var i = 0; i < _units.Count; i++) { var responseStatus = response.Tasks[i].Status; diff --git a/src/AzureExtension/DevBox/Models/DevBoxOperationResponseHeader.cs b/src/AzureExtension/DevBox/Models/DevBoxOperationResponseHeader.cs index 403e4e78..9b759e3c 100644 --- a/src/AzureExtension/DevBox/Models/DevBoxOperationResponseHeader.cs +++ b/src/AzureExtension/DevBox/Models/DevBoxOperationResponseHeader.cs @@ -6,7 +6,7 @@ namespace AzureExtension.DevBox.Models; /// -/// Represents the header for a Dev Box long running operation. The Dev Box apis run a Location and an Operation-Location +/// Represents the header for a Dev Box long running operation. The Dev Box APIs run a Location and an Operation-Location /// value in the header. These can be used to poll the progress of the operation. /// See API documentation /// diff --git a/src/AzureExtension/DeveloperId/AuthenticationSettings.cs b/src/AzureExtension/DeveloperId/AuthenticationSettings.cs index 643b8fd0..d9cc0ebd 100644 --- a/src/AzureExtension/DeveloperId/AuthenticationSettings.cs +++ b/src/AzureExtension/DeveloperId/AuthenticationSettings.cs @@ -7,8 +7,8 @@ namespace DevHomeAzureExtension.DeveloperId; public class AuthenticationSettings { - private readonly string cacheFolderPathDefault = Path.Combine(Path.GetTempPath(), "AzureExtension"); - private string? cacheFolderPath; + private readonly string _cacheFolderPathDefault = Path.Combine(Path.GetTempPath(), "AzureExtension"); + private string? _cacheFolderPath; public string Authority { @@ -37,8 +37,8 @@ public string CacheFileName public string CacheDir { - get => cacheFolderPath is null ? cacheFolderPathDefault : cacheFolderPath; - private set => cacheFolderPath = string.IsNullOrEmpty(value) ? cacheFolderPathDefault : value; + get => _cacheFolderPath is null ? _cacheFolderPathDefault : _cacheFolderPath; + private set => _cacheFolderPath = string.IsNullOrEmpty(value) ? _cacheFolderPathDefault : value; } public string Scopes @@ -66,7 +66,7 @@ public void InitializeSettings() TenantId = string.Empty; RedirectURI = "ms-appx-web://microsoft.aad.brokerplugin/{0}"; CacheFileName = "msal_cache"; - CacheDir = ApplicationData.Current != null ? ApplicationData.Current.LocalFolder.Path : cacheFolderPathDefault; + CacheDir = ApplicationData.Current != null ? ApplicationData.Current.LocalFolder.Path : _cacheFolderPathDefault; Scopes = "499b84ac-1321-427f-aa17-267ca6975798/.default"; } } diff --git a/src/AzureExtension/DeveloperId/DeveloperIdProvider.cs b/src/AzureExtension/DeveloperId/DeveloperIdProvider.cs index 2dfc375b..406a77e2 100644 --- a/src/AzureExtension/DeveloperId/DeveloperIdProvider.cs +++ b/src/AzureExtension/DeveloperId/DeveloperIdProvider.cs @@ -12,9 +12,9 @@ namespace DevHomeAzureExtension.DeveloperId; public class DeveloperIdProvider : IDeveloperIdProvider { // Locks to control access to Singleton class members. - private static readonly object DeveloperIdsLock = new(); + private static readonly object _developerIdsLock = new(); - private static readonly object AuthenticationProviderLock = new(); + private static readonly object _authenticationProviderLock = new(); // DeveloperId list containing all Logged in Ids. private List DeveloperIds @@ -28,13 +28,13 @@ private IAuthenticationHelper DeveloperIdAuthenticationHelper } // DeveloperIdProvider uses singleton pattern. - private static DeveloperIdProvider? singletonDeveloperIdProvider; + private static DeveloperIdProvider? _singletonDeveloperIdProvider; private readonly ILogger _log = Log.ForContext("SourceContext", nameof(DeveloperIdProvider)); public event TypedEventHandler? Changed; - private readonly AuthenticationExperienceKind authenticationExperienceForAzureExtension = AuthenticationExperienceKind.CustomProvider; + private readonly AuthenticationExperienceKind _authenticationExperienceForAzureExtension = AuthenticationExperienceKind.CustomProvider; public string DisplayName => "Azure"; @@ -43,7 +43,7 @@ private DeveloperIdProvider(IAuthenticationHelper authenticationHelper) { _log.Debug($"Creating DeveloperIdProvider singleton instance"); - lock (DeveloperIdsLock) + lock (_developerIdsLock) { DeveloperIds ??= new List(); @@ -96,18 +96,18 @@ public static DeveloperIdProvider GetInstance(IAuthenticationHelper? authenticat { authenticationHelper ??= new AuthenticationHelper(); - lock (AuthenticationProviderLock) + lock (_authenticationProviderLock) { - singletonDeveloperIdProvider ??= new DeveloperIdProvider(authenticationHelper); + _singletonDeveloperIdProvider ??= new DeveloperIdProvider(authenticationHelper); } - return singletonDeveloperIdProvider; + return _singletonDeveloperIdProvider; } public DeveloperIdsResult GetLoggedInDeveloperIds() { List iDeveloperIds = new(); - lock (DeveloperIdsLock) + lock (_developerIdsLock) { iDeveloperIds.AddRange(DeveloperIds); } @@ -140,7 +140,7 @@ public IAsyncOperation ShowLogonSession(WindowId windowHandle public ProviderOperationResult LogoutDeveloperId(IDeveloperId developerId) { DeveloperId? developerIdToLogout; - lock (DeveloperIdsLock) + lock (_developerIdsLock) { developerIdToLogout = DeveloperIds?.Find(e => e.LoginId == developerId.LoginId); if (developerIdToLogout == null) @@ -168,7 +168,7 @@ public ProviderOperationResult LogoutDeveloperId(IDeveloperId developerId) public IEnumerable GetLoggedInDeveloperIdsInternal() { List iDeveloperIds = new(); - lock (DeveloperIdsLock) + lock (_developerIdsLock) { iDeveloperIds.AddRange(DeveloperIds); } @@ -223,7 +223,7 @@ private DeveloperId CreateOrUpdateDeveloperId(IAccount account) } else { - lock (DeveloperIdsLock) + lock (_developerIdsLock) { DeveloperIds.Add(newDeveloperId); } @@ -248,7 +248,7 @@ private void RestoreDeveloperIds(Task> task) { DeveloperId developerId = new(loginId, loginId, loginId, string.Empty); - lock (DeveloperIdsLock) + lock (_developerIdsLock) { DeveloperIds.Add(developerId); } @@ -266,7 +266,7 @@ internal void RefreshDeveloperId(IDeveloperId developerIdInternal) public AuthenticationExperienceKind GetAuthenticationExperienceKind() { - return authenticationExperienceForAzureExtension; + return _authenticationExperienceForAzureExtension; } public void Dispose() @@ -277,7 +277,7 @@ public void Dispose() public AuthenticationState GetDeveloperIdState(IDeveloperId developerId) { DeveloperId? developerIdToFind; - lock (DeveloperIdsLock) + lock (_developerIdsLock) { developerIdToFind = DeveloperIds?.Find(e => e.LoginId == developerId.LoginId); if (developerIdToFind == null) diff --git a/src/AzureExtension/Helpers/AzureRepositoryHierarchy.cs b/src/AzureExtension/Helpers/AzureRepositoryHierarchy.cs index acd41324..255757e0 100644 --- a/src/AzureExtension/Helpers/AzureRepositoryHierarchy.cs +++ b/src/AzureExtension/Helpers/AzureRepositoryHierarchy.cs @@ -39,7 +39,7 @@ public class AzureRepositoryHierarchy /// /// A server can have multiple organizations and each organization can have multiple projects. /// Additionally, organizations and projects need to be fetched from the network. - /// This calss handles fetching the data, caching it, and searching it. + /// This class handles fetching the data, caching it, and searching it. /// public AzureRepositoryHierarchy(DeveloperId developerId) { @@ -133,7 +133,7 @@ private List QueryForOrganizations() /// /// Contacts the server to get all projects in an organization. /// - /// PRojects are returned only for this organization. + /// Projects are returned only for this organization. /// A list of projects. /// /// the Task to get the projects is added to _organizationsAndProjectTask. diff --git a/src/AzureExtension/Helpers/IconLoader.cs b/src/AzureExtension/Helpers/IconLoader.cs index 9765d7d6..20c0b04e 100644 --- a/src/AzureExtension/Helpers/IconLoader.cs +++ b/src/AzureExtension/Helpers/IconLoader.cs @@ -7,18 +7,18 @@ namespace DevHomeAzureExtension.Helpers; public class IconLoader { - private static readonly Dictionary Base64ImageRegistry = new(); + private static readonly Dictionary _base64ImageRegistry = new(); public static string GetIconAsBase64(string filename) { var log = Log.ForContext("SourceContext", nameof(IconLoader)); log.Debug($"Asking for icon: {filename}"); - if (Base64ImageRegistry.TryAdd(filename, ConvertIconToDataString(filename))) + if (_base64ImageRegistry.TryAdd(filename, ConvertIconToDataString(filename))) { log.Debug($"The icon {filename} was converted and is now stored."); } - return Base64ImageRegistry[filename]; + return _base64ImageRegistry[filename]; } private static string ConvertIconToDataString(string fileName) diff --git a/src/AzureExtension/Notifications/NotificationManager.cs b/src/AzureExtension/Notifications/NotificationManager.cs index 888e532c..2c3e90f9 100644 --- a/src/AzureExtension/Notifications/NotificationManager.cs +++ b/src/AzureExtension/Notifications/NotificationManager.cs @@ -10,13 +10,13 @@ public class NotificationManager { private readonly ILogger _log = Log.ForContext("SourceContext", nameof(NotificationManager)); - private bool isRegistered; + private bool _isRegistered; public NotificationManager(Windows.Foundation.TypedEventHandler handler) { AppNotificationManager.Default.NotificationInvoked += handler; AppNotificationManager.Default.Register(); - isRegistered = true; + _isRegistered = true; _log.Debug($"NotificationManager created and registered."); } @@ -27,10 +27,10 @@ public NotificationManager(Windows.Foundation.TypedEventHandler name; + string Microsoft.Windows.DevHome.SDK.IRepository.DisplayName => _name; public string OwningAccountName => _owningAccountName; @@ -26,7 +26,7 @@ public class DevHomeRepository : Microsoft.Windows.DevHome.SDK.IRepository public DateTimeOffset LastUpdated => _lastUpdated; - public Uri RepoUri => cloneUrl; + public Uri RepoUri => _cloneUrl; /// /// Initializes a new instance of the class. @@ -36,7 +36,7 @@ public class DevHomeRepository : Microsoft.Windows.DevHome.SDK.IRepository /// public DevHomeRepository(GitRepository gitRepository) { - name = gitRepository.Name; + _name = gitRepository.Name; Uri? localUrl; if (!Uri.TryCreate(gitRepository.WebUrl, UriKind.RelativeOrAbsolute, out localUrl)) @@ -47,7 +47,7 @@ public DevHomeRepository(GitRepository gitRepository) var repoInformation = new AzureUri(localUrl); _owningAccountName = Path.Join(repoInformation.Connection.Host, repoInformation.Organization, repoInformation.Project); - cloneUrl = localUrl; + _cloneUrl = localUrl; _isPrivate = gitRepository.ProjectReference.Visibility == Microsoft.TeamFoundation.Core.WebApi.ProjectVisibility.Private; _lastUpdated = DateTimeOffset.UtcNow; diff --git a/src/AzureExtension/Providers/RepositoryProvider.cs b/src/AzureExtension/Providers/RepositoryProvider.cs index 7c7d44d3..73a77759 100644 --- a/src/AzureExtension/Providers/RepositoryProvider.cs +++ b/src/AzureExtension/Providers/RepositoryProvider.cs @@ -38,13 +38,13 @@ public class RepositoryProvider : IRepositoryProvider2 private string _organizationToSearch = string.Empty; - private const string _defaultSearchServerName = "dev.azure.com"; + private const string DefaultSearchServerName = "dev.azure.com"; - private const string _server = "server"; + private const string Server = "server"; - private const string _organization = "organization"; + private const string Organization = "organization"; - private const string _project = "project"; + private const string Project = "project"; public string DisplayName => Resources.GetResource(@"RepositoryProviderDisplayName"); @@ -53,7 +53,7 @@ public IRandomAccessStreamReference Icon get; private set; } - public string[] SearchFieldNames => [_server, _organization]; + public string[] SearchFieldNames => [Server, Organization]; public string AskToSearchLabel => Resources.GetResource(@"SelectionOptionsPrompt"); @@ -174,11 +174,11 @@ public IAsyncOperation GetRepositoriesAsync(IDeveloperId dev var organizations = _azureHierarchy.GetOrganizations(); #pragma warning disable CA1309 // Use ordinal string comparison // Try to find any organizations with the modern URL format. - var defaultOrg = organizations.FirstOrDefault(x => x.AccountName.Equals(_defaultSearchServerName)); + var defaultOrg = organizations.FirstOrDefault(x => x.AccountName.Equals(DefaultSearchServerName)); #pragma warning restore CA1309 // Use ordinal string comparison if (defaultOrg != null) { - server = _defaultSearchServerName; + server = DefaultSearchServerName; } var repos = GetRepos(azureDeveloperId, false); @@ -372,8 +372,8 @@ public IAsyncOperation GetRepositoriesAsync(IReadOnlyD { try { - _serverToSearch = fieldValues.ContainsKey(_server) ? fieldValues[_server] : string.Empty; - _organizationToSearch = fieldValues.ContainsKey(_organization) ? fieldValues[_organization] : string.Empty; + _serverToSearch = fieldValues.ContainsKey(Server) ? fieldValues[Server] : string.Empty; + _organizationToSearch = fieldValues.ContainsKey(Organization) ? fieldValues[Organization] : string.Empty; // Get access token for ADO API calls. if (developerId is not DeveloperId.DeveloperId azureDeveloperId) @@ -383,9 +383,9 @@ public IAsyncOperation GetRepositoriesAsync(IReadOnlyD } var repos = GetRepos(azureDeveloperId, true); - var projects = await GetValuesForSearchFieldAsync(fieldValues, _project, developerId); + var projects = await GetValuesForSearchFieldAsync(fieldValues, Project, developerId); - return new RepositoriesSearchResult(repos, Path.Join(_serverToSearch, _organizationToSearch), projects.ToArray(), _project); + return new RepositoriesSearchResult(repos, Path.Join(_serverToSearch, _organizationToSearch), projects.ToArray(), Project); } catch (Exception e) { @@ -410,10 +410,10 @@ private List GetRepos(DeveloperId.DeveloperId azureDeveloperId, boo var organizations = _azureHierarchy.GetOrganizations(); var orgsInModernUrlFormat = new List(); - if (_serverToSearch.Equals(_defaultSearchServerName)) + if (_serverToSearch.Equals(DefaultSearchServerName)) { // For a default search, look up all orgs with the modern URL format - orgsInModernUrlFormat = organizations.Where(x => x.AccountUri.Host.Equals(_defaultSearchServerName)).ToList(); + orgsInModernUrlFormat = organizations.Where(x => x.AccountUri.Host.Equals(DefaultSearchServerName)).ToList(); } // If the user is apart of orgs in the modern URL format, use these going forward. @@ -427,7 +427,7 @@ private List GetRepos(DeveloperId.DeveloperId azureDeveloperId, boo organizations = organizations.Where(x => x.AccountName.Equals(_organizationToSearch)).ToList(); } - Dictionary> organizationsAndProjects = new Dictionary>(); + var organizationsAndProjects = new Dictionary>(); // Establish a connection between organizations and their projects. Parallel.ForEach(organizations, organization => @@ -472,9 +472,9 @@ private List GetRepos(DeveloperId.DeveloperId azureDeveloperId, boo HashSet servers = new(); foreach (var organization in organizations) { - if (organization.AccountUri.OriginalString.Contains(_defaultSearchServerName)) + if (organization.AccountUri.OriginalString.Contains(DefaultSearchServerName)) { - servers.Add(_defaultSearchServerName); + servers.Add(DefaultSearchServerName); } else { @@ -502,7 +502,7 @@ private List GetRepos(DeveloperId.DeveloperId azureDeveloperId, boo /// A readonly list of search field names. public IReadOnlyList GetSearchFieldNames() { - return new List { _server, _organization }; + return new List { Server, Organization }; } #pragma warning disable CA1309 // Use ordinal string comparison. Make sure names match linguisticly. @@ -512,7 +512,7 @@ public IReadOnlyList GetSearchFieldNames() /// The inputs used to filter the results. /// Results will be for this search field. /// Used to access private org and project information. - /// A list of all values for fieldName that makes snes given the input. + /// A list of all values for fieldName that make sense given the input. public IAsyncOperation> GetValuesForSearchFieldAsync(IReadOnlyDictionary fieldValues, string requestedSearchField, IDeveloperId developerId) { // Get access token for ADO API calls. @@ -527,15 +527,15 @@ public IAsyncOperation> GetValuesForSearchFieldAsync(IRead return Task.Run(() => { #pragma warning disable IDE0059 // Unnecessary assignment of a value. Server isn't needed until users use on prem. - var serverToUse = fieldValues.ContainsKey(_server) ? fieldValues[_server] : string.Empty; + var serverToUse = fieldValues.ContainsKey(Server) ? fieldValues[Server] : string.Empty; #pragma warning restore IDE0059 // Unnecessary assignment of a value - var organizationToUse = fieldValues.ContainsKey(_organization) ? fieldValues[_organization] : string.Empty; - var projectToUse = fieldValues.ContainsKey(_project) ? fieldValues[_project] : string.Empty; + var organizationToUse = fieldValues.ContainsKey(Organization) ? fieldValues[Organization] : string.Empty; + var projectToUse = fieldValues.ContainsKey(Project) ? fieldValues[Project] : string.Empty; _azureHierarchy ??= new AzureRepositoryHierarchy(azureDeveloperId); - if (requestedSearchField.Equals(_server)) + if (requestedSearchField.Equals(Server)) { var organizations = _azureHierarchy.GetOrganizations(); if (!string.IsNullOrEmpty(organizationToUse)) @@ -558,7 +558,7 @@ public IAsyncOperation> GetValuesForSearchFieldAsync(IRead return servers.Order().ToList() as IReadOnlyList; } - else if (requestedSearchField.Equals(_organization)) + else if (requestedSearchField.Equals(Organization)) { // Requesting organizations and an organization is given. var organizations = _azureHierarchy.GetOrganizations(); @@ -588,9 +588,9 @@ public IAsyncOperation> GetValuesForSearchFieldAsync(IRead return organizationsToReturn.Order().ToList() as IReadOnlyList; } } - else if (requestedSearchField.Equals(_project)) + else if (requestedSearchField.Equals(Project)) { - // Because projects exist in an organization searching behavior is different for each combonation. + // Because projects exist in an organization searching behavior is different for each combination. var isOrganizationInInput = !string.IsNullOrEmpty(organizationToUse); var isProjectInInput = !string.IsNullOrEmpty(projectToUse); @@ -598,7 +598,7 @@ public IAsyncOperation> GetValuesForSearchFieldAsync(IRead { // get all projects in the organization. var organizations = _azureHierarchy.GetOrganizations().Where(x => x.AccountName.Equals(organizationToUse)).ToList(); - List projectNames = new List(); + var projectNames = new List(); foreach (var organization in organizations) { projectNames.AddRange(_azureHierarchy.GetProjectsAsync(organization).Result.Select(x => x.Name)); @@ -612,7 +612,7 @@ public IAsyncOperation> GetValuesForSearchFieldAsync(IRead // Get all projects with the same name in all organizations. // This does mean the project drop down might have duplicate names. var organizations = _azureHierarchy.GetOrganizations(); - List projectNames = new List(); + var projectNames = new List(); foreach (var organization in organizations) { projectNames.AddRange(_azureHierarchy.GetProjectsAsync(organization).Result.Where(x => x.Name.Equals(projectToUse)).Select(x => x.Name)); @@ -628,7 +628,7 @@ public IAsyncOperation> GetValuesForSearchFieldAsync(IRead // otherwise, return nothing. var organizations = _azureHierarchy.GetOrganizations().Where(x => x.AccountName.Equals(organizationToUse)).ToList(); - List projectNames = new List(); + var projectNames = new List(); foreach (var organization in organizations) { projectNames.AddRange(_azureHierarchy.GetProjectsAsync(organization).Result.Select(x => x.Name)); @@ -664,7 +664,7 @@ public string GetDefaultValueFor(string field, IDeveloperId developerId) var organizations = _azureHierarchy.GetOrganizations(); // Get a default server name. - if (field.Equals(_server, StringComparison.OrdinalIgnoreCase)) + if (field.Equals(Server, StringComparison.OrdinalIgnoreCase)) { if (organizations.FirstOrDefault(x => x.AccountUri.OriginalString.Contains("dev.azure.com")) != null) { @@ -685,7 +685,7 @@ public string GetDefaultValueFor(string field, IDeveloperId developerId) } // get the default organization - if (field.Equals(_organization, StringComparison.OrdinalIgnoreCase)) + if (field.Equals(Organization, StringComparison.OrdinalIgnoreCase)) { var maybeNewOrganization = organizations.FirstOrDefault(x => x.AccountUri.OriginalString.Contains("dev.azure.com", StringComparison.OrdinalIgnoreCase)); if (maybeNewOrganization != null) diff --git a/src/AzureExtension/Providers/SettingsUIController.cs b/src/AzureExtension/Providers/SettingsUIController.cs index 3d650e62..ee78d9a1 100644 --- a/src/AzureExtension/Providers/SettingsUIController.cs +++ b/src/AzureExtension/Providers/SettingsUIController.cs @@ -92,10 +92,10 @@ public IAsyncOperation OnAction(string action, string i { try { - var adapativeCardPayload = JsonSerializer.Deserialize(action, SettingsAdaptiveCardPayloadSourceGeneration.Default.SettingsActionAdaptiveCardActionPayload); - if (adapativeCardPayload is not null) + var adaptiveCardPayload = JsonSerializer.Deserialize(action, SettingsAdaptiveCardPayloadSourceGeneration.Default.SettingsActionAdaptiveCardActionPayload); + if (adaptiveCardPayload is not null) { - if (adapativeCardPayload.IsClearOpenAIKeyAction) + if (adaptiveCardPayload.IsClearOpenAIKeyAction) { Log.Information("Clearing OpenAI key"); _aiCredentialService.RemoveCredentials(OpenAIDevContainerQuickStartProjectProvider.LoginId, OpenAIDevContainerQuickStartProjectProvider.LoginId); diff --git a/src/AzureExtension/QuickStartPlayground/DependencyUIController.cs b/src/AzureExtension/QuickStartPlayground/DependencyUIController.cs index 24c6e582..5647f02f 100644 --- a/src/AzureExtension/QuickStartPlayground/DependencyUIController.cs +++ b/src/AzureExtension/QuickStartPlayground/DependencyUIController.cs @@ -13,26 +13,26 @@ namespace AzureExtension.QuickStartPlayground; public sealed partial class DependencyUIController : IExtensionAdaptiveCardSession2 { - private static readonly Dictionary> Dependencies = new() + private static readonly Dictionary> _dependencies = new() { { "Docker Desktop", new("Docker Desktop", "https://docs.docker.com/desktop/install/windows-install") }, { "Microsoft Visual Studio Code", new("Visual Studio Code", "https://code.visualstudio.com/Download") }, { "ms-vscode-remote.remote-containers", new("Visual Studio Code Remote Containers Extension", "https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers") }, }; - private static readonly Dictionary> DockerAndVSCodePathEntries = new() + private static readonly Dictionary> _dockerAndVSCodePathEntries = new() { { "Docker Desktop", new() { "docker.exe" } }, { "Microsoft Visual Studio Code", new() { "code.cmd", "code-insiders.cmd" } }, }; - private static readonly HashSet DockerAndVSCode = + private static readonly HashSet _dockerAndVSCode = [ "Docker Desktop", "Microsoft Visual Studio Code", ]; - private static readonly HashSet DevContainerExtension = + private static readonly HashSet _devContainerExtension = [ "ms-vscode-remote.remote-containers", ]; @@ -45,7 +45,7 @@ public sealed partial class DependencyUIController : IExtensionAdaptiveCardSessi public static QuickStartProjectAdaptiveCardResult GetAdaptiveCard(IInstalledAppsService installedAppsService) { - var missingDependencies = installedAppsService.CheckForMissingDependencies([], DockerAndVSCode, DevContainerExtension, DockerAndVSCodePathEntries).Result; + var missingDependencies = installedAppsService.CheckForMissingDependencies([], _dockerAndVSCode, _devContainerExtension, _dockerAndVSCodePathEntries).Result; if (missingDependencies.Count == 0) { return null!; @@ -63,7 +63,7 @@ public DependencyUIController(HashSet missingDependencies, IInstalledApp public ProviderOperationResult Initialize(IExtensionAdaptiveCard extensionUI) { _extensionUI = extensionUI; - var missingDependenciesJson = CreateMissingDependenciesJson(_missingDependencies, Dependencies); + var missingDependenciesJson = CreateMissingDependenciesJson(_missingDependencies, _dependencies); return _extensionUI.Update(GetTemplate(), missingDependenciesJson, string.Empty); } @@ -205,22 +205,22 @@ public IAsyncOperation OnAction(string action, string i { return Task.Run(() => { - var adapativeCardPayload = JsonSerializer.Deserialize(action, DependencyAdaptiveCardPayloadSourceGeneration.Default.DependencyAdaptiveCardPayload); - if (adapativeCardPayload is not null) + var adaptiveCardPayload = JsonSerializer.Deserialize(action, DependencyAdaptiveCardPayloadSourceGeneration.Default.DependencyAdaptiveCardPayload); + if (adaptiveCardPayload is not null) { - if (adapativeCardPayload.IsCancelAction) + if (adaptiveCardPayload.IsCancelAction) { // Inform Dev Home using the stopped event that the user canceled, // but the result of OnAction is we successfully handled the user clicking cancel. Stopped?.Invoke(this, new(new ProviderOperationResult(ProviderOperationStatus.Failure, new OperationCanceledException(), string.Empty, string.Empty), string.Empty)); return new ProviderOperationResult(ProviderOperationStatus.Success, null, string.Empty, string.Empty); } - else if (adapativeCardPayload.IsRefreshAction) + else if (adaptiveCardPayload.IsRefreshAction) { - // Requery the installed apps service to see what dependencies are missing (if any) and update the UI. + // Re-query the installed apps service to see what dependencies are missing (if any) and update the UI. if (_installedAppsService is not null) { - _missingDependencies = _installedAppsService.CheckForMissingDependencies([], DockerAndVSCode, DevContainerExtension, DockerAndVSCodePathEntries).Result; + _missingDependencies = _installedAppsService.CheckForMissingDependencies([], _dockerAndVSCode, _devContainerExtension, _dockerAndVSCodePathEntries).Result; if (_missingDependencies.Count == 0) { var result = new ProviderOperationResult(ProviderOperationStatus.Success, null, "All dependencies are installed", string.Empty); @@ -229,22 +229,22 @@ public IAsyncOperation OnAction(string action, string i return result; } - var missingDependenciesJson = CreateMissingDependenciesJson(_missingDependencies, Dependencies); + var missingDependenciesJson = CreateMissingDependenciesJson(_missingDependencies, _dependencies); _extensionUI?.Update(GetTemplate(), missingDependenciesJson, string.Empty); } return new ProviderOperationResult(ProviderOperationStatus.Failure, null, "OnAction() failed during Refresh", "InstalledApps service is null"); } - else if (adapativeCardPayload.IsOpenUrlAction) + else if (adaptiveCardPayload.IsOpenUrlAction) { - if (adapativeCardPayload.Url is null) + if (adaptiveCardPayload.Url is null) { return new ProviderOperationResult(ProviderOperationStatus.Failure, null, "OnAction() for opening URL failed", "url is null"); } // Open the URL in the default browser. - var browser = Process.Start(new ProcessStartInfo(adapativeCardPayload.Url) { UseShellExecute = true }); + var browser = Process.Start(new ProcessStartInfo(adaptiveCardPayload.Url) { UseShellExecute = true }); if (browser is null) { return new ProviderOperationResult(ProviderOperationStatus.Failure, null, "OnAction() for opening URL failed", "Process.Start returned null"); @@ -259,7 +259,7 @@ public IAsyncOperation OnAction(string action, string i } else { - return new ProviderOperationResult(ProviderOperationStatus.Failure, null, "OnAction() called with invalid action", "adapativeCardPayload is null"); + return new ProviderOperationResult(ProviderOperationStatus.Failure, null, "OnAction() called with invalid action", "adaptiveCardPayload is null"); } }).AsAsyncOperation(); } diff --git a/src/AzureExtension/QuickStartPlayground/DevContainerGenerationOperation.cs b/src/AzureExtension/QuickStartPlayground/DevContainerGenerationOperation.cs index d5497f7c..b40370ec 100644 --- a/src/AzureExtension/QuickStartPlayground/DevContainerGenerationOperation.cs +++ b/src/AzureExtension/QuickStartPlayground/DevContainerGenerationOperation.cs @@ -139,9 +139,9 @@ public IAsyncOperationWithProgress -/// This class contains helper methods to perform vector database-like operations on the -/// sample projects. The extension uses this class to help find the reference sample that should -/// be used for the user's prompt. + +/// +/// This class contains helper methods to perform vector database-like operations on the +/// sample projects. The extension uses this class to help find the reference sample that should +/// be used for the user's prompt. /// public static class EmbeddingsCalc { private static double CalcCosineSimilarity(ReadOnlyMemory a, ReadOnlyMemory b) { try - { + { return TensorPrimitives.CosineSimilarity(a.Span, b.Span); } catch (Exception) diff --git a/src/AzureExtension/QuickStartPlayground/ExtensionInitializationUIController.cs b/src/AzureExtension/QuickStartPlayground/ExtensionInitializationUIController.cs index 87042790..2483fd99 100644 --- a/src/AzureExtension/QuickStartPlayground/ExtensionInitializationUIController.cs +++ b/src/AzureExtension/QuickStartPlayground/ExtensionInitializationUIController.cs @@ -45,9 +45,9 @@ public ProviderOperationResult Initialize(IExtensionAdaptiveCard extensionUI) private ProviderOperationResult RenderAdaptiveCard() { - var adpativeCardResult = _adaptiveCardsToDisplay[_currentCardIndex]; - adpativeCardResult.AdaptiveCardSession.Stopped += AdaptiveCardSessionStopped; - return adpativeCardResult.AdaptiveCardSession.Initialize(_extensionUI); + var adaptiveCardResult = _adaptiveCardsToDisplay[_currentCardIndex]; + adaptiveCardResult.AdaptiveCardSession.Stopped += AdaptiveCardSessionStopped; + return adaptiveCardResult.AdaptiveCardSession.Initialize(_extensionUI); } private void AdaptiveCardSessionStopped(IExtensionAdaptiveCardSession2 sender, ExtensionAdaptiveCardSessionStoppedEventArgs args) diff --git a/src/AzureExtension/QuickStartPlayground/InstalledAppsService.cs b/src/AzureExtension/QuickStartPlayground/InstalledAppsService.cs index 09f9623b..ca64fe84 100644 --- a/src/AzureExtension/QuickStartPlayground/InstalledAppsService.cs +++ b/src/AzureExtension/QuickStartPlayground/InstalledAppsService.cs @@ -60,7 +60,7 @@ private async Task> CheckForMissingWin32AppsAsync(HashSet CheckForMissingVSCodeExtensions(HashSet vsCodeExtensions) { @@ -99,7 +99,7 @@ private static HashSet CheckForMissingVSCodeExtensions(HashSet v throw new TimeoutException("code.cmd --list-extensions command didn't return after 30 seconds."); } - var extensions = output.Split(Separator, StringSplitOptions.RemoveEmptyEntries); + var extensions = output.Split(_separator, StringSplitOptions.RemoveEmptyEntries); // Check if any of the desired extensions are in the list. foreach (var extension in missingExtensions) diff --git a/src/AzureExtension/QuickStartPlayground/OpenAIDevContainerQuickStartProjectProvider.cs b/src/AzureExtension/QuickStartPlayground/OpenAIDevContainerQuickStartProjectProvider.cs index 62b3563f..887893d1 100644 --- a/src/AzureExtension/QuickStartPlayground/OpenAIDevContainerQuickStartProjectProvider.cs +++ b/src/AzureExtension/QuickStartPlayground/OpenAIDevContainerQuickStartProjectProvider.cs @@ -119,10 +119,10 @@ public unsafe IAsyncOperation OnAction(string action, s { return Task.Run(() => { - var adapativeCardPayload = JsonSerializer.Deserialize(action, AIAdaptiveCardPayloadSourceGeneration.Default.AIAdaptiveCardActionPayload); - if (adapativeCardPayload is not null) + var adaptiveCardPayload = JsonSerializer.Deserialize(action, AIAdaptiveCardPayloadSourceGeneration.Default.AIAdaptiveCardActionPayload); + if (adaptiveCardPayload is not null) { - if (adapativeCardPayload.IsSubmitAction) + if (adaptiveCardPayload.IsSubmitAction) { var inputPayload = JsonSerializer.Deserialize(inputs, AIAdaptiveCardPayloadSourceGeneration.Default.AIAdaptiveCardInputPayload); if (inputPayload is not null && !string.IsNullOrEmpty(inputPayload.Key)) @@ -145,7 +145,7 @@ public unsafe IAsyncOperation OnAction(string action, s return new ProviderOperationResult(ProviderOperationStatus.Failure, null, Resources.GetResource(@"QuickstartPlayground_OpenAI_AdaptiveCard_InvalidAPIKey"), "API Key is null"); } } - else if (adapativeCardPayload.IsCancelAction) + else if (adaptiveCardPayload.IsCancelAction) { // Inform Dev Home using the stopped event that the user canceled, // but the result of OnAction is we successfully handled the user clicking cancel. @@ -159,7 +159,7 @@ public unsafe IAsyncOperation OnAction(string action, s } else { - return new ProviderOperationResult(ProviderOperationStatus.Failure, null, Resources.GetResource(@"QuickstartPlayground_OpenAI_AdaptiveCard_InvalidAPIKey"), "adapativeCardPayload is null"); + return new ProviderOperationResult(ProviderOperationStatus.Failure, null, Resources.GetResource(@"QuickstartPlayground_OpenAI_AdaptiveCard_InvalidAPIKey"), "adaptiveCardPayload is null"); } }).AsAsyncOperation(); } diff --git a/src/AzureExtension/Services/DevBox/ARMTokenService.cs b/src/AzureExtension/Services/DevBox/ARMTokenService.cs index f13f0a17..c45b9e65 100644 --- a/src/AzureExtension/Services/DevBox/ARMTokenService.cs +++ b/src/AzureExtension/Services/DevBox/ARMTokenService.cs @@ -11,7 +11,7 @@ namespace AzureExtension.Services.DevBox; /// /// Implementation of the Azure Resource Manager (ARM) token service. -/// It is a wrapper leveraging Developer ID's silent token aquiring +/// It is a wrapper leveraging Developer ID's silent token acquiring /// function, with the scope needed for ARM /// public class ARMTokenService : IArmTokenService diff --git a/src/AzureExtension/Services/DevBox/DataTokenService.cs b/src/AzureExtension/Services/DevBox/DataTokenService.cs index d23415dc..84611789 100644 --- a/src/AzureExtension/Services/DevBox/DataTokenService.cs +++ b/src/AzureExtension/Services/DevBox/DataTokenService.cs @@ -11,7 +11,7 @@ namespace AzureExtension.Services.DevBox; /// /// Implementation of Dev Box Data Plane token service. -/// It is a wrapper leveraging Developer ID's silent token aquiring +/// It is a wrapper leveraging Developer ID's silent token acquiring /// function with the scope needed for the plane. /// public class DataTokenService : IDataTokenService diff --git a/src/AzureExtension/Services/DevBox/DevBoxCreationManager.cs b/src/AzureExtension/Services/DevBox/DevBoxCreationManager.cs index ab038750..f30939e0 100644 --- a/src/AzureExtension/Services/DevBox/DevBoxCreationManager.cs +++ b/src/AzureExtension/Services/DevBox/DevBoxCreationManager.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.Collections.Generic; using System.Text.Json; using AzureExtension.Contracts; using AzureExtension.DevBox; @@ -10,7 +9,6 @@ using DevHomeAzureExtension.Helpers; using Microsoft.Windows.DevHome.SDK; using Serilog; -using static System.Windows.Forms.VisualStyles.VisualStyleElement; namespace AzureExtension.Services.DevBox; diff --git a/src/AzureExtension/Services/DevBox/DevBoxOperationWatcher.cs b/src/AzureExtension/Services/DevBox/DevBoxOperationWatcher.cs index 5f1d3f44..2e0e1512 100644 --- a/src/AzureExtension/Services/DevBox/DevBoxOperationWatcher.cs +++ b/src/AzureExtension/Services/DevBox/DevBoxOperationWatcher.cs @@ -7,7 +7,7 @@ using AzureExtension.DevBox.DevBoxJsonToCsClasses; using AzureExtension.DevBox.Exceptions; using AzureExtension.DevBox.Models; -using Microsoft.Windows.DevHome.SDK; +using Microsoft.Windows.DevHome.SDK; using Serilog; using Windows.System.Threading; @@ -34,8 +34,8 @@ public class DevBoxOperationWatcher : IDevBoxOperationWatcher private readonly ITimeSpanService _timeSpanService; private const string DevBoxOperationWatcherName = nameof(DevBoxManagementService); - - private readonly ILogger _log = Log.ForContext("SourceContext", nameof(DevBoxOperationWatcher)); + + private readonly ILogger _log = Log.ForContext("SourceContext", nameof(DevBoxOperationWatcher)); private readonly Dictionary _operationWatcherTimers = new(); @@ -56,7 +56,7 @@ public DevBoxOperationWatcher(IDevBoxManagementService managementService, ITimeS /// While the timer is running, the operationId is used as the key in a dictionary to keep track of the timer. This is done so that we can cancel the timer once /// the operation is no longer running, at which point the operationId is removed from the dictionary. The operationId are guid values so they can be used as keys in the dictionary. /// - public void StartDevCenterOperationMonitor(IDeveloperId developerId, Uri operationUri, Guid operationId, DevBoxActionToPerform actionToPerform, Action completionCallback) + public void StartDevCenterOperationMonitor(IDeveloperId developerId, Uri operationUri, Guid operationId, DevBoxActionToPerform actionToPerform, Action completionCallback) { var interval = _timeSpanService.GetPeriodIntervalBasedOnAction(actionToPerform); @@ -71,8 +71,8 @@ public void StartDevCenterOperationMonitor(IDeveloperId developerId, Uri operati async (ThreadPoolTimer timer) => { try - { - _log.Information($"Starting Dev Box operation with action {actionToPerform}, Uri: {operationUri}, Id: {operationId}"); + { + _log.Information($"Starting Dev Box operation with action {actionToPerform}, Uri: {operationUri}, Id: {operationId}"); // Query the Dev Center for the status of the Dev Box operation. var result = await _managementService.HttpsRequestToDataPlane(operationUri, developerId, HttpMethod.Get, null); @@ -114,10 +114,10 @@ public void StartDevCenterOperationMonitor(IDeveloperId developerId, Uri operati /// stops the extensions process or reboots the machine we will lose the Uri to the operation. In those cases we use this method to monitor the provisioning status of the Dev Box. /// When Dev Boxes are requested by Dev Home the objects we get back from the Dev Center will have a provisioning state of "Provisioning". This indicates /// that the Dev Box is still being created. We use a ThreadPoolTimer to periodically query the Dev Center for the provisioning status of the Dev Box. Once this is set to - /// "Succeeded" we know that the Dev Box has been created and we can stop the timer and invoke the completionCallback. DevBoxInstance Id's are Guid's so we can use them as keys in for + /// "Succeeded" we know that the Dev Box has been created and we can stop the timer and invoke the completionCallback. DevBoxInstance Id's are GUIDs so we can use them as keys in for /// operationWaterTimers. /// - public void StartDevBoxProvisioningStatusMonitor(IDeveloperId developerId, DevBoxActionToPerform actionToPerform, DevBoxInstance devBoxInstance, Action completionCallback) + public void StartDevBoxProvisioningStatusMonitor(IDeveloperId developerId, DevBoxActionToPerform actionToPerform, DevBoxInstance devBoxInstance, Action completionCallback) { var interval = _timeSpanService.GetPeriodIntervalBasedOnAction(actionToPerform); var devBoxId = Guid.Parse(devBoxInstance.Id); @@ -133,15 +133,15 @@ public void StartDevBoxProvisioningStatusMonitor(IDeveloperId developerId, DevBo async (ThreadPoolTimer timer) => { try - { - _log.Information($"Starting the provisioning monitor for Dev Box with Name: '{devBoxInstance.DisplayName}' , Id: '{devBoxInstance.Id}'"); + { + _log.Information($"Starting the provisioning monitor for Dev Box with Name: '{devBoxInstance.DisplayName}' , Id: '{devBoxInstance.Id}'"); // Query the Dev Center for the provisioning status of the Dev Box. This is needed for when the Dev Box was created outside of Dev Home. var devBoxUri = $"{devBoxInstance.DevBoxState.Uri}?{Constants.APIVersion}"; var result = await _managementService.HttpsRequestToDataPlane(new Uri(devBoxUri), developerId, HttpMethod.Get, null); - var devBoxState = JsonSerializer.Deserialize(result.JsonResponseRoot.ToString(), Constants.JsonOptions)!; - - // If the Dev Box is no longer being created, update the state for Dev Homes UI and end the timer. + var devBoxState = JsonSerializer.Deserialize(result.JsonResponseRoot.ToString(), Constants.JsonOptions)!; + + // If the Dev Box is no longer being created, update the state for Dev Homes UI and end the timer. if (!(devBoxState.ProvisioningState == Constants.DevBoxProvisioningStates.Creating || devBoxState.ProvisioningState == Constants.DevBoxProvisioningStates.Provisioning)) { _log.Information($"Dev Box provisioning now completed."); @@ -221,13 +221,13 @@ private void AddTimerToWatcherItem(Guid id, ThreadPoolTimer timer) _operationWatcherTimers[id].Timer = timer; } - } - - public bool IsIdBeingWatched(Guid id) - { - lock (_lock) - { - return _operationWatcherTimers.ContainsKey(id); - } - } + } + + public bool IsIdBeingWatched(Guid id) + { + lock (_lock) + { + return _operationWatcherTimers.ContainsKey(id); + } + } } diff --git a/src/AzureExtension/Services/DevBox/PackagesService.cs b/src/AzureExtension/Services/DevBox/PackagesService.cs index 5ca5edb0..1bb3f1a5 100644 --- a/src/AzureExtension/Services/DevBox/PackagesService.cs +++ b/src/AzureExtension/Services/DevBox/PackagesService.cs @@ -2,14 +2,13 @@ // Licensed under the MIT License. using AzureExtension.Contracts; -using Microsoft.Windows.DevHome.SDK; using Windows.ApplicationModel; namespace AzureExtension.Services.DevBox; public class PackagesService : IPackagesService { - private Windows.Management.Deployment.PackageManager _packageManager = new(); + private readonly Windows.Management.Deployment.PackageManager _packageManager = new(); public bool IsPackageInstalled(string packageName) { @@ -19,7 +18,7 @@ public bool IsPackageInstalled(string packageName) public PackageVersion GetPackageInstalledVersion(string packageName) { - PackageVersion version = new PackageVersion(0, 0, 0, 0); + var version = new PackageVersion(0, 0, 0, 0); var currentPackage = _packageManager.FindPackagesForUser(string.Empty, packageName).FirstOrDefault(); if (currentPackage != null) { diff --git a/src/AzureExtension/Services/DevBox/TimeSpanService.cs b/src/AzureExtension/Services/DevBox/TimeSpanService.cs index 70f38d81..8af58701 100644 --- a/src/AzureExtension/Services/DevBox/TimeSpanService.cs +++ b/src/AzureExtension/Services/DevBox/TimeSpanService.cs @@ -17,8 +17,8 @@ public class TimeSpanService : ITimeSpanService /// Get the period interval based on the action to perform. /// /// - /// The creation operation of a Dev Box takes longer than all other operations and can take between 20 to 65 minutes. Possibly more depending - /// on how the users IT admin has configured their Dev Center subscriptions. All other operations take less than 5 minutes except for the delete + /// The creation operation of a Dev Box takes longer than all other operations and can take between 20 to 65 minutes. Possibly more depending + /// on how the users IT admin has configured their Dev Center subscriptions. All other operations take less than 5 minutes except for the delete /// operation which can take up to 10 minutes. /// /// The Dev Box action that is being performed. E.g Start diff --git a/src/AzureExtension/Strings/en-US/Resources.resw b/src/AzureExtension/Strings/en-US/Resources.resw index 5a4cc9f6..c6f254d7 100644 --- a/src/AzureExtension/Strings/en-US/Resources.resw +++ b/src/AzureExtension/Strings/en-US/Resources.resw @@ -431,7 +431,7 @@ User session expired. Please login again from the settings page. Error shown when user session has expired and re-login is needed. - + Unable to retrieve all Dev Boxes for {0}. Error shown when Dev Boxes retrival failed. diff --git a/test/AzureExtension/DataStore/DataStoreTestsSetup.cs b/test/AzureExtension/DataStore/DataStoreTestsSetup.cs index cd0ae6f3..ffb0126b 100644 --- a/test/AzureExtension/DataStore/DataStoreTestsSetup.cs +++ b/test/AzureExtension/DataStore/DataStoreTestsSetup.cs @@ -12,12 +12,12 @@ public TestContext? TestContext set; } - private TestOptions testOptions = new(); + private TestOptions _testOptions = new(); private TestOptions TestOptions { - get => testOptions; - set => testOptions = value; + get => _testOptions; + set => _testOptions = value; } [TestInitialize] diff --git a/test/AzureExtension/DevBox/DevBoxTests.cs b/test/AzureExtension/DevBox/DevBoxTests.cs index 6c7e43db..2f7cee35 100644 --- a/test/AzureExtension/DevBox/DevBoxTests.cs +++ b/test/AzureExtension/DevBox/DevBoxTests.cs @@ -2,15 +2,11 @@ // Licensed under the MIT License. using System.Text.Json; -using System.Threading; -using AzureExtension.Contracts; using AzureExtension.DevBox; using AzureExtension.DevBox.DevBoxJsonToCsClasses; using AzureExtension.DevBox.Models; -using AzureExtension.Services.DevBox; using AzureExtension.Test.DevBox; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; using Microsoft.Windows.DevHome.SDK; namespace DevHomeAzureExtension.Test; diff --git a/test/AzureExtension/DevBox/DevBoxTestsSetup.cs b/test/AzureExtension/DevBox/DevBoxTestsSetup.cs index 5f7a7160..438ee1c3 100644 --- a/test/AzureExtension/DevBox/DevBoxTestsSetup.cs +++ b/test/AzureExtension/DevBox/DevBoxTestsSetup.cs @@ -1,13 +1,10 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.Collections.ObjectModel; using System.Net; using System.Net.Http.Headers; -using System.Reflection.Metadata.Ecma335; using AzureExtension.Contracts; using AzureExtension.DevBox; -using AzureExtension.DevBox.DevBoxJsonToCsClasses; using AzureExtension.DevBox.Models; using AzureExtension.Services.DevBox; using AzureExtension.Test.DevBox; @@ -22,7 +19,7 @@ namespace DevHomeAzureExtension.Test; [TestClass] public partial class DevBoxTests : IDisposable { - private readonly Mock mockFactory = new(); + private readonly Mock _mockFactory = new(); public Mock HttpHandler { get; set; } = new(MockBehavior.Strict); @@ -33,12 +30,12 @@ public TestContext? TestContext get; set; } - private TestOptions testOptions = new(); + private TestOptions _testOptions = new(); private TestOptions TestOptions { - get => testOptions; - set => testOptions = value; + get => _testOptions; + set => _testOptions = value; } /// @@ -161,7 +158,7 @@ public static HttpResponseHeaders GetHeadersMock() return headers; } - private HttpClient mockHttpClient = new(); + private HttpClient _mockHttpClient = new(); public void UpdateHttpClientResponseMock(List returnList) { @@ -200,12 +197,12 @@ public void TestInitialize() TestHelpers.ConfigureTestLog(TestOptions, TestContext!); // Create an HttpClient using the mocked handler - mockHttpClient = new HttpClient(HttpHandler.Object); + _mockHttpClient = new HttpClient(HttpHandler.Object); - mockFactory.Setup(f => f.CreateClient(It.IsAny())) - .Returns(mockHttpClient); + _mockFactory.Setup(f => f.CreateClient(It.IsAny())) + .Returns(_mockHttpClient); - TestHost = BuildhostContainer(); + TestHost = BuildHostContainer(); } [TestCleanup] @@ -220,7 +217,7 @@ public void Dispose() GC.SuppressFinalize(this); } - public IHost BuildhostContainer() + public IHost BuildHostContainer() { return Host. CreateDefaultBuilder(). @@ -231,8 +228,8 @@ public IHost BuildhostContainer() }). ConfigureServices((context, services) => { - services.AddSingleton(mockFactory.Object); - services.AddSingleton(mockHttpClient); + services.AddSingleton(_mockFactory.Object); + services.AddSingleton(_mockHttpClient); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/test/AzureExtension/DevBox/TimeSpanServiceMock.cs b/test/AzureExtension/DevBox/TimeSpanServiceMock.cs index 37c530bb..f7a11b0e 100644 --- a/test/AzureExtension/DevBox/TimeSpanServiceMock.cs +++ b/test/AzureExtension/DevBox/TimeSpanServiceMock.cs @@ -1,11 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using AzureExtension.Contracts; using AzureExtension.DevBox; diff --git a/test/AzureExtension/DeveloperId/DeveloperIdTests.cs b/test/AzureExtension/DeveloperId/DeveloperIdTests.cs index 75f9b6fd..45847198 100644 --- a/test/AzureExtension/DeveloperId/DeveloperIdTests.cs +++ b/test/AzureExtension/DeveloperId/DeveloperIdTests.cs @@ -32,7 +32,7 @@ public void DevId_Manual_LoginLogoutEvents() authProvider.LogoutDeveloperId(devIds.First()); // Wait 1 sec for the Logout event. - AuthenticationEventTriggered.WaitOne(1000); + _authenticationEventTriggered.WaitOne(1000); // Get the list of DeveloperIds devIds = authProvider.GetLoggedInDeveloperIdsInternal(); diff --git a/test/AzureExtension/DeveloperId/DeveloperIdTestsSetup.cs b/test/AzureExtension/DeveloperId/DeveloperIdTestsSetup.cs index c12d749d..50a5b5c4 100644 --- a/test/AzureExtension/DeveloperId/DeveloperIdTestsSetup.cs +++ b/test/AzureExtension/DeveloperId/DeveloperIdTestsSetup.cs @@ -14,12 +14,12 @@ public TestContext? TestContext set; } - private TestOptions testOptions = new(); + private TestOptions _testOptions = new(); private TestOptions TestOptions { - get => testOptions; - set => testOptions = value; + get => _testOptions; + set => _testOptions = value; } [TestInitialize] @@ -36,19 +36,19 @@ public void Cleanup() TestHelpers.CleanupTempTestOptions(TestOptions, TestContext!); } - private static readonly Semaphore AuthenticationEventTriggered = new(initialCount: 0, maximumCount: 1); + private static readonly Semaphore _authenticationEventTriggered = new(initialCount: 0, maximumCount: 1); public void AuthenticationEvent(object? sender, IDeveloperId developerId) { if (developerId.LoginId is not null) { - AuthenticationEventTriggered.Release(); + _authenticationEventTriggered.Release(); } } public void Dispose() { GC.SuppressFinalize(this); - AuthenticationEventTriggered.Dispose(); + _authenticationEventTriggered.Dispose(); } } diff --git a/test/AzureExtension/DeveloperId/Mocks/MockAuthenticationHelper.cs b/test/AzureExtension/DeveloperId/Mocks/MockAuthenticationHelper.cs index 74fe9bcb..e8dfdb60 100644 --- a/test/AzureExtension/DeveloperId/Mocks/MockAuthenticationHelper.cs +++ b/test/AzureExtension/DeveloperId/Mocks/MockAuthenticationHelper.cs @@ -9,8 +9,8 @@ namespace DevHomeAzureExtension.Test.DeveloperId.Mocks; internal sealed class MockAuthenticationHelper : IAuthenticationHelper { - private readonly List loginIds = new(); - + private readonly List _loginIds = new(); + private static readonly string[] _scopes = new[] { "scope1", "scope2" }; public AuthenticationSettings MicrosoftEntraIdSettings @@ -27,13 +27,13 @@ public AuthenticationSettings MicrosoftEntraIdSettings public void PopulateCache() { - loginIds.Add("testAccount"); + _loginIds.Add("testAccount"); } public async Task> GetAllStoredLoginIdsAsync() { await Task.Run(() => { }); - return loginIds; + return _loginIds; } public async void InitializePublicClientAppForWAMBrokerAsync() @@ -55,7 +55,7 @@ public void InitializePublicClientApplicationBuilder() public async Task LoginDeveloperAccount(string[] scopes) { await Task.Run(() => { }); - AuthenticationResult authResult = new AuthenticationResult( + var authResult = new AuthenticationResult( accessToken: string.Empty, isExtendedLifeTimeToken: false, uniqueId: string.Empty, diff --git a/test/AzureExtension/Helpers/TestSetupHelpers.cs b/test/AzureExtension/Helpers/TestSetupHelpers.cs index 931e2562..57c81352 100644 --- a/test/AzureExtension/Helpers/TestSetupHelpers.cs +++ b/test/AzureExtension/Helpers/TestSetupHelpers.cs @@ -13,7 +13,7 @@ public partial class TestHelpers { private const string DataBaseFileName = "AzureExtension-Test.db"; private const string LogFileName = "AzureExtension-{now}.dhlog"; - private static readonly TimeSpan CleanupRetryWaitTime = TimeSpan.FromSeconds(10); + private static readonly TimeSpan _cleanupRetryWaitTime = TimeSpan.FromSeconds(10); public static void CleanupTempTestOptions(TestOptions options, TestContext context) { @@ -42,7 +42,7 @@ public static void CleanupTempTestOptions(TestOptions options, TestContext conte // log is done writing. This was leading to random intermittent test failures due // to the log file being in use. If we encounter an IOException, wait a few seconds // and try again. - Thread.Sleep(CleanupRetryWaitTime); + Thread.Sleep(_cleanupRetryWaitTime); context?.WriteLine($"Cleanup: Retrying Deleting folder {path}"); Directory.Delete(path, true); diff --git a/test/AzureExtension/RepositoryProvider/RepositoryProviderTests.cs b/test/AzureExtension/RepositoryProvider/RepositoryProviderTests.cs index 80c87f58..49bd6499 100644 --- a/test/AzureExtension/RepositoryProvider/RepositoryProviderTests.cs +++ b/test/AzureExtension/RepositoryProvider/RepositoryProviderTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using DevHomeAzureExtension.Client; -using Microsoft.Extensions.Hosting; using Microsoft.Windows.DevHome.SDK; namespace DevHomeAzureExtension.Test; diff --git a/test/AzureExtension/RepositoryProvider/RepositoryProviderTestsSetup.cs b/test/AzureExtension/RepositoryProvider/RepositoryProviderTestsSetup.cs index 81f28edd..e5490fdf 100644 --- a/test/AzureExtension/RepositoryProvider/RepositoryProviderTestsSetup.cs +++ b/test/AzureExtension/RepositoryProvider/RepositoryProviderTestsSetup.cs @@ -40,12 +40,12 @@ public TestContext? TestContext set; } - private TestOptions testOptions = new(); + private TestOptions _testOptions = new(); private TestOptions TestOptions { - get => testOptions; - set => testOptions = value; + get => _testOptions; + set => _testOptions = value; } [TestInitialize] From fe77336957bae35364ec7a9270c5bf9f6a0206d7 Mon Sep 17 00:00:00 2001 From: Kristen Schau <47155823+krschau@users.noreply.github.com> Date: Wed, 26 Jun 2024 14:23:48 -0400 Subject: [PATCH 03/11] Consolate on DevHomeAzureExtension namespace (#230) --- src/AzureExtension/AzureExtension.cs | 5 ++-- .../Contracts/IAICredentialService.cs | 2 +- .../Contracts/IArmTokenService.cs | 2 +- .../Contracts/IAzureOpenAIService.cs | 2 +- .../Contracts/IDataTokenService.cs | 2 +- .../Contracts/IDevBoxAuthService.cs | 2 +- .../Contracts/IDevBoxCreationManager.cs | 6 ++--- .../Contracts/IDevBoxManagementService.cs | 6 ++--- .../Contracts/IDevBoxOperationWatcher.cs | 6 ++--- .../Contracts/IInstalledAppsService.cs | 2 +- .../Contracts/IPackagesService.cs | 2 +- .../Contracts/ITimeSpanService.cs | 4 ++-- src/AzureExtension/DevBox/Constants.cs | 6 ++--- src/AzureExtension/DevBox/DevBoxInstance.cs | 12 +++++----- .../DevBoxCreationPoolName.cs | 2 +- .../DevBoxHardwareProfile.cs | 2 +- .../DevBoxImageReference.cs | 2 +- .../DevBoxLongRunningOperation.cs | 4 ++-- .../DevBoxMachineState.cs | 2 +- .../DevBoxJsonToCsClasses/DevBoxMachines.cs | 2 +- .../DevBoxJsonToCsClasses/DevBoxOperation.cs | 4 ++-- .../DevBoxOperationList.cs | 2 +- .../DevBoxOperationResult.cs | 2 +- .../DevBoxJsonToCsClasses/DevBoxOsDisk.cs | 2 +- .../DevBoxJsonToCsClasses/DevBoxPool.cs | 2 +- .../DevBoxJsonToCsClasses/DevBoxPoolRoot.cs | 2 +- .../DevBoxPoolStopOnDisconnect.cs | 2 +- .../DevBoxJsonToCsClasses/DevBoxProject.cs | 2 +- .../DevBoxProjectProperties.cs | 2 +- .../DevBoxJsonToCsClasses/DevBoxProjects.cs | 2 +- .../DevBoxRemoteConnectionData.cs | 2 +- .../DevBoxStorageProfile.cs | 2 +- .../JsonSourceGenerationContext.cs | 4 ++-- src/AzureExtension/DevBox/DevBoxProvider.cs | 8 +++---- .../Exceptions/DevBoxCreationException.cs | 2 +- .../Exceptions/DevBoxNameInvalidException.cs | 2 +- .../DevBoxOperationMonitorException.cs | 2 +- .../DevBoxProjectNameInvalidException.cs | 2 +- .../WingetConfigurationException.cs | 2 +- .../Helpers/AdaptiveCardJSONToCSClass.cs | 2 +- .../DevBox/Helpers/DevBoxOperationHelper.cs | 2 +- .../DevCenterOperationStatusConverter.cs | 4 ++-- .../DevBox/Helpers/TaskJSONToCSClasses.cs | 2 +- .../DevBox/Helpers/TaskYAMLToCSClasses.cs | 2 +- .../WaitingForUserAdaptiveCardSession.cs | 2 +- .../DevBox/Helpers/WingetConfigWrapper.cs | 10 ++++---- .../Models/CreateComputeSystemOperation.cs | 6 ++--- .../Models/CreationAdaptiveCardSession.cs | 2 +- .../DevBox/Models/DevBoxCreationParameters.cs | 2 +- .../DevBox/Models/DevBoxHttpsRequestResult.cs | 2 +- .../Models/DevBoxOperationResponseHeader.cs | 2 +- .../Models/DevBoxOperationWatcherTimer.cs | 4 ++-- .../Models/DevBoxProjectAndPoolContainer.cs | 4 ++-- .../DevBox/Models/DevCenterOperationBase.cs | 2 +- .../Helpers/AzureRepositoryHierarchy.cs | 7 +++--- .../Providers/RepositoryProvider.cs | 1 - .../Providers/SettingsProvider.cs | 4 ++-- .../Providers/SettingsUIController.cs | 6 ++--- .../AICredentialService.cs | 4 ++-- ...AIDevContainerQuickStartProjectProvider.cs | 4 ++-- .../AzureOpenAIService.cs | 4 ++-- .../QuickStartPlayground/Completions.cs | 4 ++-- .../DependencyUIController.cs | 4 ++-- .../DevContainerGenerationOperation.cs | 4 ++-- .../DevContainerProjectFeedback.cs | 2 +- .../DevContainerProjectHost.cs | 2 +- .../DevContainerQuickStartProjectProvider.cs | 4 ++-- .../DockerProgressUIController.cs | 2 +- .../QuickStartPlayground/DockerValidation.cs | 2 +- .../QuickStartPlayground/EmbeddingsCalc.cs | 2 +- .../ExtensionInitializationUIController.cs | 2 +- .../InstalledAppsService.cs | 4 ++-- ...AIDevContainerQuickStartProjectProvider.cs | 4 ++-- .../QuickStartPlayground/ServiceExtensions.cs | 4 ++-- .../QuickStartPlayground/TrainingSample.cs | 2 +- .../Services/DevBox/ARMTokenService.cs | 8 +++---- .../Services/DevBox/AuthService.cs | 4 ++-- .../Services/DevBox/DataTokenService.cs | 8 +++---- .../Services/DevBox/DevBoxCreationManager.cs | 19 +++++++-------- .../DevBox/DevBoxManagementService.cs | 23 +++++++++---------- .../Services/DevBox/DevBoxOperationWatcher.cs | 21 +++++++++-------- .../Services/DevBox/PackagesService.cs | 4 ++-- .../Services/DevBox/TimeSpanService.cs | 13 ++++++----- src/AzureExtensionServer/Program.cs | 12 +++++----- .../DevBox/ArmTestTokenService.cs | 4 ++-- .../DevBox/DataTestTokenService.cs | 4 ++-- test/AzureExtension/DevBox/DevBoxTests.cs | 8 +++---- .../AzureExtension/DevBox/DevBoxTestsSetup.cs | 10 ++++---- .../AzureExtension/DevBox/EmptyDeveloperId.cs | 2 +- .../DevBox/TimeSpanServiceMock.cs | 6 ++--- .../Mocks/MockAuthenticationSettings.cs | 2 +- 91 files changed, 194 insertions(+), 195 deletions(-) diff --git a/src/AzureExtension/AzureExtension.cs b/src/AzureExtension/AzureExtension.cs index c5af3d70..af243f78 100644 --- a/src/AzureExtension/AzureExtension.cs +++ b/src/AzureExtension/AzureExtension.cs @@ -2,11 +2,10 @@ // Licensed under the MIT License. using System.Runtime.InteropServices; -using AzureExtension.DevBox; -using AzureExtension.Providers; -using AzureExtension.QuickStartPlayground; +using DevHomeAzureExtension.DevBox; using DevHomeAzureExtension.DeveloperId; using DevHomeAzureExtension.Providers; +using DevHomeAzureExtension.QuickStartPlayground; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Windows.DevHome.SDK; diff --git a/src/AzureExtension/Contracts/IAICredentialService.cs b/src/AzureExtension/Contracts/IAICredentialService.cs index 21a99e03..64530900 100644 --- a/src/AzureExtension/Contracts/IAICredentialService.cs +++ b/src/AzureExtension/Contracts/IAICredentialService.cs @@ -3,7 +3,7 @@ using System.Security; -namespace AzureExtension.Contracts; +namespace DevHomeAzureExtension.Contracts; public interface IAICredentialService { diff --git a/src/AzureExtension/Contracts/IArmTokenService.cs b/src/AzureExtension/Contracts/IArmTokenService.cs index 13e454e7..2bd0e895 100644 --- a/src/AzureExtension/Contracts/IArmTokenService.cs +++ b/src/AzureExtension/Contracts/IArmTokenService.cs @@ -3,7 +3,7 @@ using Microsoft.Windows.DevHome.SDK; -namespace AzureExtension.Contracts; +namespace DevHomeAzureExtension.Contracts; // ARMTokenService is a service that provides an Azure Resource Manager (ARM) token. public interface IArmTokenService diff --git a/src/AzureExtension/Contracts/IAzureOpenAIService.cs b/src/AzureExtension/Contracts/IAzureOpenAIService.cs index 472be76b..30c5ece9 100644 --- a/src/AzureExtension/Contracts/IAzureOpenAIService.cs +++ b/src/AzureExtension/Contracts/IAzureOpenAIService.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace AzureExtension.Contracts; +namespace DevHomeAzureExtension.Contracts; public enum OpenAIEndpoint { diff --git a/src/AzureExtension/Contracts/IDataTokenService.cs b/src/AzureExtension/Contracts/IDataTokenService.cs index 73bedb2c..bfbd4758 100644 --- a/src/AzureExtension/Contracts/IDataTokenService.cs +++ b/src/AzureExtension/Contracts/IDataTokenService.cs @@ -3,7 +3,7 @@ using Microsoft.Windows.DevHome.SDK; -namespace AzureExtension.Contracts; +namespace DevHomeAzureExtension.Contracts; public interface IDataTokenService { diff --git a/src/AzureExtension/Contracts/IDevBoxAuthService.cs b/src/AzureExtension/Contracts/IDevBoxAuthService.cs index da86cddc..fc1ef8a2 100644 --- a/src/AzureExtension/Contracts/IDevBoxAuthService.cs +++ b/src/AzureExtension/Contracts/IDevBoxAuthService.cs @@ -3,7 +3,7 @@ using Microsoft.Windows.DevHome.SDK; -namespace AzureExtension.Contracts; +namespace DevHomeAzureExtension.Contracts; public interface IDevBoxAuthService { diff --git a/src/AzureExtension/Contracts/IDevBoxCreationManager.cs b/src/AzureExtension/Contracts/IDevBoxCreationManager.cs index 7939ea58..be3793c5 100644 --- a/src/AzureExtension/Contracts/IDevBoxCreationManager.cs +++ b/src/AzureExtension/Contracts/IDevBoxCreationManager.cs @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using AzureExtension.DevBox; -using AzureExtension.DevBox.Models; +using DevHomeAzureExtension.DevBox; +using DevHomeAzureExtension.DevBox.Models; using Microsoft.Windows.DevHome.SDK; -namespace AzureExtension.Contracts; +namespace DevHomeAzureExtension.Contracts; public interface IDevBoxCreationManager { diff --git a/src/AzureExtension/Contracts/IDevBoxManagementService.cs b/src/AzureExtension/Contracts/IDevBoxManagementService.cs index 3c38d0e6..351e1894 100644 --- a/src/AzureExtension/Contracts/IDevBoxManagementService.cs +++ b/src/AzureExtension/Contracts/IDevBoxManagementService.cs @@ -2,11 +2,11 @@ // Licensed under the MIT License. using System.Text.Json; -using AzureExtension.DevBox.DevBoxJsonToCsClasses; -using AzureExtension.DevBox.Models; +using DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; +using DevHomeAzureExtension.DevBox.Models; using Microsoft.Windows.DevHome.SDK; -namespace AzureExtension.Contracts; +namespace DevHomeAzureExtension.Contracts; public interface IDevBoxManagementService { diff --git a/src/AzureExtension/Contracts/IDevBoxOperationWatcher.cs b/src/AzureExtension/Contracts/IDevBoxOperationWatcher.cs index fa8af34d..eaf9a069 100644 --- a/src/AzureExtension/Contracts/IDevBoxOperationWatcher.cs +++ b/src/AzureExtension/Contracts/IDevBoxOperationWatcher.cs @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using AzureExtension.DevBox; -using AzureExtension.DevBox.Models; +using DevHomeAzureExtension.DevBox; +using DevHomeAzureExtension.DevBox.Models; using Microsoft.Windows.DevHome.SDK; -namespace AzureExtension.Contracts; +namespace DevHomeAzureExtension.Contracts; /// /// Interface used to watch Dev Box operations asynchronously that take place in the Dev Center. diff --git a/src/AzureExtension/Contracts/IInstalledAppsService.cs b/src/AzureExtension/Contracts/IInstalledAppsService.cs index 85b53251..00392ad8 100644 --- a/src/AzureExtension/Contracts/IInstalledAppsService.cs +++ b/src/AzureExtension/Contracts/IInstalledAppsService.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace AzureExtension.Contracts; +namespace DevHomeAzureExtension.Contracts; public interface IInstalledAppsService { diff --git a/src/AzureExtension/Contracts/IPackagesService.cs b/src/AzureExtension/Contracts/IPackagesService.cs index 0f4c9e31..0b3ed82f 100644 --- a/src/AzureExtension/Contracts/IPackagesService.cs +++ b/src/AzureExtension/Contracts/IPackagesService.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace AzureExtension.Contracts; +namespace DevHomeAzureExtension.Contracts; using Windows.ApplicationModel; diff --git a/src/AzureExtension/Contracts/ITimeSpanService.cs b/src/AzureExtension/Contracts/ITimeSpanService.cs index 43535e8f..03713ce7 100644 --- a/src/AzureExtension/Contracts/ITimeSpanService.cs +++ b/src/AzureExtension/Contracts/ITimeSpanService.cs @@ -1,9 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using AzureExtension.DevBox; +using DevHomeAzureExtension.DevBox; -namespace AzureExtension.Contracts; +namespace DevHomeAzureExtension.Contracts; /// /// Interface that an operation watcher can use to get the time span based on the action to perform. diff --git a/src/AzureExtension/DevBox/Constants.cs b/src/AzureExtension/DevBox/Constants.cs index 1bd607fd..8c463e2c 100644 --- a/src/AzureExtension/DevBox/Constants.cs +++ b/src/AzureExtension/DevBox/Constants.cs @@ -2,13 +2,13 @@ // Licensed under the MIT License. using System.Text.Json; -using AzureExtension.DevBox.DevBoxJsonToCsClasses; -using AzureExtension.DevBox.Helpers; +using DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; +using DevHomeAzureExtension.DevBox.Helpers; using DevHomeAzureExtension.Helpers; using Windows.ApplicationModel; using Windows.Storage; -namespace AzureExtension.DevBox; +namespace DevHomeAzureExtension.DevBox; public static class Constants { diff --git a/src/AzureExtension/DevBox/DevBoxInstance.cs b/src/AzureExtension/DevBox/DevBoxInstance.cs index 98e665b4..a424e059 100644 --- a/src/AzureExtension/DevBox/DevBoxInstance.cs +++ b/src/AzureExtension/DevBox/DevBoxInstance.cs @@ -7,18 +7,18 @@ using System.Text; using System.Text.Json; using System.Web; -using AzureExtension.Contracts; -using AzureExtension.DevBox.DevBoxJsonToCsClasses; -using AzureExtension.DevBox.Helpers; -using AzureExtension.DevBox.Models; -using AzureExtension.Services.DevBox; +using DevHomeAzureExtension.Contracts; +using DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; +using DevHomeAzureExtension.DevBox.Helpers; +using DevHomeAzureExtension.DevBox.Models; using DevHomeAzureExtension.Helpers; +using DevHomeAzureExtension.Services.DevBox; using Microsoft.Windows.DevHome.SDK; using Serilog; using Windows.ApplicationModel; using Windows.Foundation; -namespace AzureExtension.DevBox; +namespace DevHomeAzureExtension.DevBox; public delegate DevBoxInstance DevBoxInstanceFactory(IDeveloperId developerId, DevBoxMachineState devBoxJson); diff --git a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxCreationPoolName.cs b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxCreationPoolName.cs index a5a16397..cae1e50a 100644 --- a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxCreationPoolName.cs +++ b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxCreationPoolName.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace AzureExtension.DevBox.DevBoxJsonToCsClasses; +namespace DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; /// /// This is used to represent the json object that we send with the "create Dev Box" request. diff --git a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxHardwareProfile.cs b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxHardwareProfile.cs index a1b4e463..a919b05f 100644 --- a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxHardwareProfile.cs +++ b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxHardwareProfile.cs @@ -3,7 +3,7 @@ using System.Text.Json.Serialization; -namespace AzureExtension.DevBox.DevBoxJsonToCsClasses; +namespace DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; /// /// Represents the HardwareProfile object within a response from a Dev Box rest API call. diff --git a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxImageReference.cs b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxImageReference.cs index 88189b5c..94c2b967 100644 --- a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxImageReference.cs +++ b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxImageReference.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace AzureExtension.DevBox.DevBoxJsonToCsClasses; +namespace DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; /// /// Represents the ImageReference object within a response from a Dev Box rest API call. diff --git a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxLongRunningOperation.cs b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxLongRunningOperation.cs index 2cf1d5d3..e305c4a7 100644 --- a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxLongRunningOperation.cs +++ b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxLongRunningOperation.cs @@ -1,9 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using AzureExtension.DevBox.Models; +using DevHomeAzureExtension.DevBox.Models; -namespace AzureExtension.DevBox.DevBoxJsonToCsClasses; +namespace DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; /// /// https://azure.github.io/typespec-azure/docs/howtos/Azure%20Core/long-running-operations diff --git a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxMachineState.cs b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxMachineState.cs index c2ea3075..e01691b2 100644 --- a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxMachineState.cs +++ b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxMachineState.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace AzureExtension.DevBox.DevBoxJsonToCsClasses; +namespace DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; /// /// This class represents the state of a DevBox machine. It is the class representation of diff --git a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxMachines.cs b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxMachines.cs index 8f94a501..6e451325 100644 --- a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxMachines.cs +++ b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxMachines.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace AzureExtension.DevBox.DevBoxJsonToCsClasses; +namespace DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; /// /// Represents the response from the Dev Center API for getting a list of DevBoxes diff --git a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxOperation.cs b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxOperation.cs index d18c24af..5443b774 100644 --- a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxOperation.cs +++ b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxOperation.cs @@ -2,9 +2,9 @@ // Licensed under the MIT License. using System.Text.Json.Serialization; -using AzureExtension.DevBox.Models; +using DevHomeAzureExtension.DevBox.Models; -namespace AzureExtension.DevBox.DevBoxJsonToCsClasses; +namespace DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; /// /// Represents the DevBoxOperation object within a response from a Dev Box rest API call. diff --git a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxOperationList.cs b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxOperationList.cs index 5e55ff9f..c0a8ff9a 100644 --- a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxOperationList.cs +++ b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxOperationList.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace AzureExtension.DevBox.DevBoxJsonToCsClasses; +namespace DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; /// /// Represents a list of DevBoxOperation object's within a response from a Dev Box rest API call. diff --git a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxOperationResult.cs b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxOperationResult.cs index 1ccc47d1..d2bb7ab3 100644 --- a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxOperationResult.cs +++ b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxOperationResult.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace AzureExtension.DevBox.DevBoxJsonToCsClasses; +namespace DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; /// /// Represents a DevBoxOperationResult object within a response from a Dev Box rest API call. diff --git a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxOsDisk.cs b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxOsDisk.cs index 514d1eef..8e091cca 100644 --- a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxOsDisk.cs +++ b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxOsDisk.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace AzureExtension.DevBox.DevBoxJsonToCsClasses; +namespace DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; /// /// Represents a OsDisk object within a response from a Dev Box rest API call. diff --git a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxPool.cs b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxPool.cs index 5c351e2d..f8006564 100644 --- a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxPool.cs +++ b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxPool.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace AzureExtension.DevBox.DevBoxJsonToCsClasses; +namespace DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; /// /// Represents a Pool object within a response from a Dev Box rest API call. diff --git a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxPoolRoot.cs b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxPoolRoot.cs index 9d8ff1d5..4b5b64a6 100644 --- a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxPoolRoot.cs +++ b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxPoolRoot.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace AzureExtension.DevBox.DevBoxJsonToCsClasses; +namespace DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; /// /// Represents a list of Pool object's within a response from a Dev Box rest API call. diff --git a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxPoolStopOnDisconnect.cs b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxPoolStopOnDisconnect.cs index 7d5c95ad..b9f9e083 100644 --- a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxPoolStopOnDisconnect.cs +++ b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxPoolStopOnDisconnect.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace AzureExtension.DevBox.DevBoxJsonToCsClasses; +namespace DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; /// /// Represents a StopOnDisconnect object within a response from a Dev Box rest API call. diff --git a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxProject.cs b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxProject.cs index f5da8a67..64df5e3c 100644 --- a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxProject.cs +++ b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxProject.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace AzureExtension.DevBox.DevBoxJsonToCsClasses; +namespace DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; /// /// Represents a DevBoxProject object within a response from a Dev Box rest API call. diff --git a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxProjectProperties.cs b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxProjectProperties.cs index 7b6d2b5f..d29bbc9f 100644 --- a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxProjectProperties.cs +++ b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxProjectProperties.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace AzureExtension.DevBox.DevBoxJsonToCsClasses; +namespace DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; /// /// Represents a ProjectProperties object within a response from a Dev Box rest API call. diff --git a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxProjects.cs b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxProjects.cs index a43f2703..428d2b02 100644 --- a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxProjects.cs +++ b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxProjects.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace AzureExtension.DevBox.DevBoxJsonToCsClasses; +namespace DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; /// /// Represents a List of DevBoxProject objects within a response from a Dev Box rest API call. diff --git a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxRemoteConnectionData.cs b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxRemoteConnectionData.cs index 3f85ff4e..c4f053b5 100644 --- a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxRemoteConnectionData.cs +++ b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxRemoteConnectionData.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace AzureExtension.DevBox.DevBoxJsonToCsClasses; +namespace DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; /// /// Represents the data returned from a request to retrieve the remote connection information for a Dev Box. diff --git a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxStorageProfile.cs b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxStorageProfile.cs index 95c54287..82e0c18d 100644 --- a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxStorageProfile.cs +++ b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxStorageProfile.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace AzureExtension.DevBox.DevBoxJsonToCsClasses; +namespace DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; /// /// Represents the StorageProfile object within a response from a Dev Box rest API call. diff --git a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/JsonSourceGenerationContext.cs b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/JsonSourceGenerationContext.cs index bbe4b3ba..968cc8a6 100644 --- a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/JsonSourceGenerationContext.cs +++ b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/JsonSourceGenerationContext.cs @@ -2,9 +2,9 @@ // Licensed under the MIT License. using System.Text.Json.Serialization; -using AzureExtension.DevBox.Models; +using DevHomeAzureExtension.DevBox.Models; -namespace AzureExtension.DevBox.DevBoxJsonToCsClasses; +namespace DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; /// /// Used to generate the source code for the classes that we deserialize Json to objects for the DevBox feature. diff --git a/src/AzureExtension/DevBox/DevBoxProvider.cs b/src/AzureExtension/DevBox/DevBoxProvider.cs index 9d64bc78..d7a75ddc 100644 --- a/src/AzureExtension/DevBox/DevBoxProvider.cs +++ b/src/AzureExtension/DevBox/DevBoxProvider.cs @@ -4,15 +4,15 @@ using System.Collections.Concurrent; using System.Text; using System.Text.Json; -using AzureExtension.Contracts; -using AzureExtension.DevBox.DevBoxJsonToCsClasses; -using AzureExtension.DevBox.Models; +using DevHomeAzureExtension.Contracts; +using DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; +using DevHomeAzureExtension.DevBox.Models; using DevHomeAzureExtension.Helpers; using Microsoft.Windows.DevHome.SDK; using Serilog; using Windows.Foundation; -namespace AzureExtension.DevBox; +namespace DevHomeAzureExtension.DevBox; /// /// Implements the IComputeSystemProvider interface to provide DevBoxes as ComputeSystems. diff --git a/src/AzureExtension/DevBox/Exceptions/DevBoxCreationException.cs b/src/AzureExtension/DevBox/Exceptions/DevBoxCreationException.cs index f793d628..a3c6091f 100644 --- a/src/AzureExtension/DevBox/Exceptions/DevBoxCreationException.cs +++ b/src/AzureExtension/DevBox/Exceptions/DevBoxCreationException.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace AzureExtension.DevBox.Exceptions; +namespace DevHomeAzureExtension.DevBox.Exceptions; public class DevBoxCreationException : Exception { diff --git a/src/AzureExtension/DevBox/Exceptions/DevBoxNameInvalidException.cs b/src/AzureExtension/DevBox/Exceptions/DevBoxNameInvalidException.cs index 1fed875f..3bce3405 100644 --- a/src/AzureExtension/DevBox/Exceptions/DevBoxNameInvalidException.cs +++ b/src/AzureExtension/DevBox/Exceptions/DevBoxNameInvalidException.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace AzureExtension.DevBox.Exceptions; +namespace DevHomeAzureExtension.DevBox.Exceptions; public class DevBoxNameInvalidException : Exception { diff --git a/src/AzureExtension/DevBox/Exceptions/DevBoxOperationMonitorException.cs b/src/AzureExtension/DevBox/Exceptions/DevBoxOperationMonitorException.cs index 74c28f33..fa8070d2 100644 --- a/src/AzureExtension/DevBox/Exceptions/DevBoxOperationMonitorException.cs +++ b/src/AzureExtension/DevBox/Exceptions/DevBoxOperationMonitorException.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace AzureExtension.DevBox.Exceptions; +namespace DevHomeAzureExtension.DevBox.Exceptions; public class DevBoxOperationMonitorException : Exception { diff --git a/src/AzureExtension/DevBox/Exceptions/DevBoxProjectNameInvalidException.cs b/src/AzureExtension/DevBox/Exceptions/DevBoxProjectNameInvalidException.cs index 4da2d4ec..6241f441 100644 --- a/src/AzureExtension/DevBox/Exceptions/DevBoxProjectNameInvalidException.cs +++ b/src/AzureExtension/DevBox/Exceptions/DevBoxProjectNameInvalidException.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace AzureExtension.DevBox.Exceptions; +namespace DevHomeAzureExtension.DevBox.Exceptions; public class DevBoxProjectNameInvalidException : Exception { diff --git a/src/AzureExtension/DevBox/Exceptions/WingetConfigurationException.cs b/src/AzureExtension/DevBox/Exceptions/WingetConfigurationException.cs index f0e720d3..addbd266 100644 --- a/src/AzureExtension/DevBox/Exceptions/WingetConfigurationException.cs +++ b/src/AzureExtension/DevBox/Exceptions/WingetConfigurationException.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace AzureExtension.DevBox.Exceptions; +namespace DevHomeAzureExtension.DevBox.Exceptions; public class WingetConfigurationException : Exception { diff --git a/src/AzureExtension/DevBox/Helpers/AdaptiveCardJSONToCSClass.cs b/src/AzureExtension/DevBox/Helpers/AdaptiveCardJSONToCSClass.cs index 52169a12..908bd3f4 100644 --- a/src/AzureExtension/DevBox/Helpers/AdaptiveCardJSONToCSClass.cs +++ b/src/AzureExtension/DevBox/Helpers/AdaptiveCardJSONToCSClass.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace AzureExtension.DevBox.Helpers; +namespace DevHomeAzureExtension.DevBox.Helpers; public class AdaptiveCardJSONToCSClass { diff --git a/src/AzureExtension/DevBox/Helpers/DevBoxOperationHelper.cs b/src/AzureExtension/DevBox/Helpers/DevBoxOperationHelper.cs index 3923376d..ac86546d 100644 --- a/src/AzureExtension/DevBox/Helpers/DevBoxOperationHelper.cs +++ b/src/AzureExtension/DevBox/Helpers/DevBoxOperationHelper.cs @@ -3,7 +3,7 @@ using Microsoft.Windows.DevHome.SDK; -namespace AzureExtension.DevBox.Helpers; +namespace DevHomeAzureExtension.DevBox.Helpers; public static class DevBoxOperationHelper { diff --git a/src/AzureExtension/DevBox/Helpers/DevCenterOperationStatusConverter.cs b/src/AzureExtension/DevBox/Helpers/DevCenterOperationStatusConverter.cs index 69ac7048..e9cdce8c 100644 --- a/src/AzureExtension/DevBox/Helpers/DevCenterOperationStatusConverter.cs +++ b/src/AzureExtension/DevBox/Helpers/DevCenterOperationStatusConverter.cs @@ -3,9 +3,9 @@ using System.Text.Json; using System.Text.Json.Serialization; -using AzureExtension.DevBox.Models; +using DevHomeAzureExtension.DevBox.Models; -namespace AzureExtension.DevBox.Helpers; +namespace DevHomeAzureExtension.DevBox.Helpers; /// /// Custom JSON converter for . diff --git a/src/AzureExtension/DevBox/Helpers/TaskJSONToCSClasses.cs b/src/AzureExtension/DevBox/Helpers/TaskJSONToCSClasses.cs index ad3e7445..914d89bb 100644 --- a/src/AzureExtension/DevBox/Helpers/TaskJSONToCSClasses.cs +++ b/src/AzureExtension/DevBox/Helpers/TaskJSONToCSClasses.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace AzureExtension.DevBox.Helpers; +namespace DevHomeAzureExtension.DevBox.Helpers; // Example of a task JSON response that will be deserialized into the BaseClass class // { diff --git a/src/AzureExtension/DevBox/Helpers/TaskYAMLToCSClasses.cs b/src/AzureExtension/DevBox/Helpers/TaskYAMLToCSClasses.cs index 9e94b924..3e8cf8c8 100644 --- a/src/AzureExtension/DevBox/Helpers/TaskYAMLToCSClasses.cs +++ b/src/AzureExtension/DevBox/Helpers/TaskYAMLToCSClasses.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace AzureExtension.DevBox.Helpers; +namespace DevHomeAzureExtension.DevBox.Helpers; // Note: For every addition of a new resource type, the corresponding classes should be added here. // Example of a YAML file that will be deserialized diff --git a/src/AzureExtension/DevBox/Helpers/WaitingForUserAdaptiveCardSession.cs b/src/AzureExtension/DevBox/Helpers/WaitingForUserAdaptiveCardSession.cs index fdbca1c1..f8f7aed0 100644 --- a/src/AzureExtension/DevBox/Helpers/WaitingForUserAdaptiveCardSession.cs +++ b/src/AzureExtension/DevBox/Helpers/WaitingForUserAdaptiveCardSession.cs @@ -9,7 +9,7 @@ using Serilog; using Windows.Foundation; -namespace AzureExtension.DevBox.Helpers; +namespace DevHomeAzureExtension.DevBox.Helpers; public class WaitingForUserAdaptiveCardSession : IExtensionAdaptiveCardSession2, IDisposable { diff --git a/src/AzureExtension/DevBox/Helpers/WingetConfigWrapper.cs b/src/AzureExtension/DevBox/Helpers/WingetConfigWrapper.cs index 5fe286fb..2a358d8a 100644 --- a/src/AzureExtension/DevBox/Helpers/WingetConfigWrapper.cs +++ b/src/AzureExtension/DevBox/Helpers/WingetConfigWrapper.cs @@ -3,16 +3,16 @@ using System.Text; using System.Text.Json; -using AzureExtension.Contracts; -using AzureExtension.DevBox.DevBoxJsonToCsClasses; -using AzureExtension.DevBox.Exceptions; +using DevHomeAzureExtension.Contracts; +using DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; +using DevHomeAzureExtension.DevBox.Exceptions; using DevHomeAzureExtension.Helpers; using Microsoft.Windows.DevHome.SDK; using Windows.Foundation; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; -namespace AzureExtension.DevBox.Helpers; +namespace DevHomeAzureExtension.DevBox.Helpers; public class WingetConfigWrapper : IApplyConfigurationOperation, IDisposable { @@ -32,7 +32,7 @@ public class WingetConfigWrapper : IApplyConfigurationOperation, IDisposable public const string WingetTaskJsonTaskStart = @"{ ""name"": ""winget"", - ""runAs"": ""User"", + ""runAs"": ""User"", ""parameters"": { ""inlineConfigurationBase64"": """; diff --git a/src/AzureExtension/DevBox/Models/CreateComputeSystemOperation.cs b/src/AzureExtension/DevBox/Models/CreateComputeSystemOperation.cs index 5bc3d47c..389970e4 100644 --- a/src/AzureExtension/DevBox/Models/CreateComputeSystemOperation.cs +++ b/src/AzureExtension/DevBox/Models/CreateComputeSystemOperation.cs @@ -1,14 +1,14 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using AzureExtension.Contracts; -using AzureExtension.DevBox.Exceptions; +using DevHomeAzureExtension.Contracts; +using DevHomeAzureExtension.DevBox.Exceptions; using DevHomeAzureExtension.Helpers; using Microsoft.Windows.DevHome.SDK; using Serilog; using Windows.Foundation; -namespace AzureExtension.DevBox.Models; +namespace DevHomeAzureExtension.DevBox.Models; public delegate CreateComputeSystemOperation CreateComputeSystemOperationFactory(IDeveloperId developerId, DevBoxCreationParameters userOptions); diff --git a/src/AzureExtension/DevBox/Models/CreationAdaptiveCardSession.cs b/src/AzureExtension/DevBox/Models/CreationAdaptiveCardSession.cs index efffa066..0de4cb11 100644 --- a/src/AzureExtension/DevBox/Models/CreationAdaptiveCardSession.cs +++ b/src/AzureExtension/DevBox/Models/CreationAdaptiveCardSession.cs @@ -9,7 +9,7 @@ using Serilog; using Windows.Foundation; -namespace AzureExtension.DevBox.Models; +namespace DevHomeAzureExtension.DevBox.Models; public enum SessionState { diff --git a/src/AzureExtension/DevBox/Models/DevBoxCreationParameters.cs b/src/AzureExtension/DevBox/Models/DevBoxCreationParameters.cs index 86851b59..7b502afc 100644 --- a/src/AzureExtension/DevBox/Models/DevBoxCreationParameters.cs +++ b/src/AzureExtension/DevBox/Models/DevBoxCreationParameters.cs @@ -4,7 +4,7 @@ using System.Globalization; using System.Text; -namespace AzureExtension.DevBox.Models; +namespace DevHomeAzureExtension.DevBox.Models; /// /// These parameters are the parameters we'll expect to be passed in from Dev Home to create a Dev Box. diff --git a/src/AzureExtension/DevBox/Models/DevBoxHttpsRequestResult.cs b/src/AzureExtension/DevBox/Models/DevBoxHttpsRequestResult.cs index 2c089f27..927beac3 100644 --- a/src/AzureExtension/DevBox/Models/DevBoxHttpsRequestResult.cs +++ b/src/AzureExtension/DevBox/Models/DevBoxHttpsRequestResult.cs @@ -3,7 +3,7 @@ using System.Text.Json; -namespace AzureExtension.DevBox.Models; +namespace DevHomeAzureExtension.DevBox.Models; /// /// Represents the result of an HTTPS request result from the Dev Center. For Dev Box operations diff --git a/src/AzureExtension/DevBox/Models/DevBoxOperationResponseHeader.cs b/src/AzureExtension/DevBox/Models/DevBoxOperationResponseHeader.cs index 9b759e3c..a47839eb 100644 --- a/src/AzureExtension/DevBox/Models/DevBoxOperationResponseHeader.cs +++ b/src/AzureExtension/DevBox/Models/DevBoxOperationResponseHeader.cs @@ -3,7 +3,7 @@ using System.Net.Http.Headers; -namespace AzureExtension.DevBox.Models; +namespace DevHomeAzureExtension.DevBox.Models; /// /// Represents the header for a Dev Box long running operation. The Dev Box APIs run a Location and an Operation-Location diff --git a/src/AzureExtension/DevBox/Models/DevBoxOperationWatcherTimer.cs b/src/AzureExtension/DevBox/Models/DevBoxOperationWatcherTimer.cs index 5ec27405..2dbb548d 100644 --- a/src/AzureExtension/DevBox/Models/DevBoxOperationWatcherTimer.cs +++ b/src/AzureExtension/DevBox/Models/DevBoxOperationWatcherTimer.cs @@ -1,10 +1,10 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using AzureExtension.Services.DevBox; +using DevHomeAzureExtension.Services.DevBox; using Windows.System.Threading; -namespace AzureExtension.DevBox.Models; +namespace DevHomeAzureExtension.DevBox.Models; /// /// Used to store information about a timer that is being stored by the . diff --git a/src/AzureExtension/DevBox/Models/DevBoxProjectAndPoolContainer.cs b/src/AzureExtension/DevBox/Models/DevBoxProjectAndPoolContainer.cs index 3d6352f0..552a5ab2 100644 --- a/src/AzureExtension/DevBox/Models/DevBoxProjectAndPoolContainer.cs +++ b/src/AzureExtension/DevBox/Models/DevBoxProjectAndPoolContainer.cs @@ -1,9 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using AzureExtension.DevBox.DevBoxJsonToCsClasses; +using DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; -namespace AzureExtension.DevBox.Models; +namespace DevHomeAzureExtension.DevBox.Models; /// /// Represents an object that contains both a DevBoxProject and a DevBoxPoolRoot. diff --git a/src/AzureExtension/DevBox/Models/DevCenterOperationBase.cs b/src/AzureExtension/DevBox/Models/DevCenterOperationBase.cs index 8964bb24..eaa05c69 100644 --- a/src/AzureExtension/DevBox/Models/DevCenterOperationBase.cs +++ b/src/AzureExtension/DevBox/Models/DevCenterOperationBase.cs @@ -3,7 +3,7 @@ using System.Text.Json.Serialization; -namespace AzureExtension.DevBox.Models; +namespace DevHomeAzureExtension.DevBox.Models; /// /// The status of a DevCenter operation. diff --git a/src/AzureExtension/Helpers/AzureRepositoryHierarchy.cs b/src/AzureExtension/Helpers/AzureRepositoryHierarchy.cs index 255757e0..abc8f68d 100644 --- a/src/AzureExtension/Helpers/AzureRepositoryHierarchy.cs +++ b/src/AzureExtension/Helpers/AzureRepositoryHierarchy.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using DevHomeAzureExtension.Client; -using DevHomeAzureExtension.DeveloperId; using Microsoft.TeamFoundation.Core.WebApi; using Microsoft.VisualStudio.Services.Account.Client; using Microsoft.VisualStudio.Services.WebApi; @@ -11,7 +10,7 @@ // In the past, an organization was known as an account. Typedef to organization to make the code easier to read. using Organization = Microsoft.VisualStudio.Services.Account.Account; -namespace AzureExtension.Helpers; +namespace DevHomeAzureExtension.Helpers; /// /// Handles the hierarchy between organizations and projects. Handles querying for both as well. @@ -29,7 +28,7 @@ public class AzureRepositoryHierarchy // 1:N Organization to project. private readonly Dictionary> _organizationsAndProjects = new(); - private readonly DeveloperId _developerId; + private readonly DeveloperId.DeveloperId _developerId; /// /// Initializes a new instance of the class. @@ -41,7 +40,7 @@ public class AzureRepositoryHierarchy /// Additionally, organizations and projects need to be fetched from the network. /// This class handles fetching the data, caching it, and searching it. /// - public AzureRepositoryHierarchy(DeveloperId developerId) + public AzureRepositoryHierarchy(DeveloperId.DeveloperId developerId) { _developerId = developerId; } diff --git a/src/AzureExtension/Providers/RepositoryProvider.cs b/src/AzureExtension/Providers/RepositoryProvider.cs index 73a77759..9ce4390d 100644 --- a/src/AzureExtension/Providers/RepositoryProvider.cs +++ b/src/AzureExtension/Providers/RepositoryProvider.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System.Security.Authentication; -using AzureExtension.Helpers; using DevHomeAzureExtension.Client; using DevHomeAzureExtension.DeveloperId; using DevHomeAzureExtension.Helpers; diff --git a/src/AzureExtension/Providers/SettingsProvider.cs b/src/AzureExtension/Providers/SettingsProvider.cs index 52a11300..197285f5 100644 --- a/src/AzureExtension/Providers/SettingsProvider.cs +++ b/src/AzureExtension/Providers/SettingsProvider.cs @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using AzureExtension.Contracts; +using DevHomeAzureExtension.Contracts; using DevHomeAzureExtension.Helpers; using Microsoft.Windows.DevHome.SDK; -namespace AzureExtension.Providers; +namespace DevHomeAzureExtension.Providers; public sealed class SettingsProvider : ISettingsProvider { diff --git a/src/AzureExtension/Providers/SettingsUIController.cs b/src/AzureExtension/Providers/SettingsUIController.cs index ee78d9a1..0b9f21b7 100644 --- a/src/AzureExtension/Providers/SettingsUIController.cs +++ b/src/AzureExtension/Providers/SettingsUIController.cs @@ -3,14 +3,14 @@ using System.Text.Json; using System.Text.Json.Serialization; -using AzureExtension.Contracts; -using AzureExtension.QuickStartPlayground; +using DevHomeAzureExtension.Contracts; using DevHomeAzureExtension.Helpers; +using DevHomeAzureExtension.QuickStartPlayground; using Microsoft.Windows.DevHome.SDK; using Serilog; using Windows.Foundation; -namespace AzureExtension.Providers; +namespace DevHomeAzureExtension.Providers; internal sealed partial class SettingsUIController : IExtensionAdaptiveCardSession { diff --git a/src/AzureExtension/QuickStartPlayground/AICredentialService.cs b/src/AzureExtension/QuickStartPlayground/AICredentialService.cs index 9bf7b23e..181f4d6e 100644 --- a/src/AzureExtension/QuickStartPlayground/AICredentialService.cs +++ b/src/AzureExtension/QuickStartPlayground/AICredentialService.cs @@ -5,12 +5,12 @@ using System.Runtime.InteropServices; using System.Security; using System.Text; -using AzureExtension.Contracts; +using DevHomeAzureExtension.Contracts; using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.Security.Credentials; -namespace AzureExtension.QuickStartPlayground; +namespace DevHomeAzureExtension.QuickStartPlayground; public sealed class AICredentialService : IAICredentialService { diff --git a/src/AzureExtension/QuickStartPlayground/AzureOpenAIDevContainerQuickStartProjectProvider.cs b/src/AzureExtension/QuickStartPlayground/AzureOpenAIDevContainerQuickStartProjectProvider.cs index 0d8963cc..9edc9341 100644 --- a/src/AzureExtension/QuickStartPlayground/AzureOpenAIDevContainerQuickStartProjectProvider.cs +++ b/src/AzureExtension/QuickStartPlayground/AzureOpenAIDevContainerQuickStartProjectProvider.cs @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using AzureExtension.Contracts; +using DevHomeAzureExtension.Contracts; using DevHomeAzureExtension.Helpers; using Microsoft.Windows.DevHome.SDK; -namespace AzureExtension.QuickStartPlayground; +namespace DevHomeAzureExtension.QuickStartPlayground; public sealed class AzureOpenAIDevContainerQuickStartProjectProvider : DevContainerQuickStartProjectProvider { diff --git a/src/AzureExtension/QuickStartPlayground/AzureOpenAIService.cs b/src/AzureExtension/QuickStartPlayground/AzureOpenAIService.cs index 596d1b5d..5d6ad31e 100644 --- a/src/AzureExtension/QuickStartPlayground/AzureOpenAIService.cs +++ b/src/AzureExtension/QuickStartPlayground/AzureOpenAIService.cs @@ -5,12 +5,12 @@ using System.Reflection; using Azure; using Azure.AI.OpenAI; -using AzureExtension.Contracts; +using DevHomeAzureExtension.Contracts; using Microsoft.ML.Tokenizers; using Newtonsoft.Json; using Serilog; -namespace AzureExtension.QuickStartPlayground; +namespace DevHomeAzureExtension.QuickStartPlayground; public sealed class AzureOpenAIService : IAzureOpenAIService { diff --git a/src/AzureExtension/QuickStartPlayground/Completions.cs b/src/AzureExtension/QuickStartPlayground/Completions.cs index e6f1f124..f3d3040b 100644 --- a/src/AzureExtension/QuickStartPlayground/Completions.cs +++ b/src/AzureExtension/QuickStartPlayground/Completions.cs @@ -1,9 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using AzureExtension.Contracts; +using DevHomeAzureExtension.Contracts; -namespace AzureExtension.QuickStartPlayground; +namespace DevHomeAzureExtension.QuickStartPlayground; public static class Completions { diff --git a/src/AzureExtension/QuickStartPlayground/DependencyUIController.cs b/src/AzureExtension/QuickStartPlayground/DependencyUIController.cs index 5647f02f..07c7e525 100644 --- a/src/AzureExtension/QuickStartPlayground/DependencyUIController.cs +++ b/src/AzureExtension/QuickStartPlayground/DependencyUIController.cs @@ -4,12 +4,12 @@ using System.Diagnostics; using System.Text.Json; using System.Text.Json.Serialization; -using AzureExtension.Contracts; +using DevHomeAzureExtension.Contracts; using DevHomeAzureExtension.Helpers; using Microsoft.Windows.DevHome.SDK; using Windows.Foundation; -namespace AzureExtension.QuickStartPlayground; +namespace DevHomeAzureExtension.QuickStartPlayground; public sealed partial class DependencyUIController : IExtensionAdaptiveCardSession2 { diff --git a/src/AzureExtension/QuickStartPlayground/DevContainerGenerationOperation.cs b/src/AzureExtension/QuickStartPlayground/DevContainerGenerationOperation.cs index b40370ec..fc8b1bf8 100644 --- a/src/AzureExtension/QuickStartPlayground/DevContainerGenerationOperation.cs +++ b/src/AzureExtension/QuickStartPlayground/DevContainerGenerationOperation.cs @@ -4,14 +4,14 @@ using System.Diagnostics; using System.Runtime.InteropServices.WindowsRuntime; using System.Text.Json; -using AzureExtension.Contracts; +using DevHomeAzureExtension.Contracts; using DevHomeAzureExtension.Helpers; using Microsoft.Windows.DevHome.SDK; using Serilog; using Windows.Foundation; using Windows.Storage; -namespace AzureExtension.QuickStartPlayground; +namespace DevHomeAzureExtension.QuickStartPlayground; public sealed class DevContainerGenerationOperation : IQuickStartProjectGenerationOperation { diff --git a/src/AzureExtension/QuickStartPlayground/DevContainerProjectFeedback.cs b/src/AzureExtension/QuickStartPlayground/DevContainerProjectFeedback.cs index dcc71ed2..9b5ddb7e 100644 --- a/src/AzureExtension/QuickStartPlayground/DevContainerProjectFeedback.cs +++ b/src/AzureExtension/QuickStartPlayground/DevContainerProjectFeedback.cs @@ -3,7 +3,7 @@ using Microsoft.Windows.DevHome.SDK; -namespace AzureExtension.QuickStartPlayground; +namespace DevHomeAzureExtension.QuickStartPlayground; public sealed class DevContainerProjectFeedback : IQuickStartProjectResultFeedbackHandler { diff --git a/src/AzureExtension/QuickStartPlayground/DevContainerProjectHost.cs b/src/AzureExtension/QuickStartPlayground/DevContainerProjectHost.cs index 2a6adc2a..fefc0e1f 100644 --- a/src/AzureExtension/QuickStartPlayground/DevContainerProjectHost.cs +++ b/src/AzureExtension/QuickStartPlayground/DevContainerProjectHost.cs @@ -5,7 +5,7 @@ using DevHomeAzureExtension.Helpers; using Microsoft.Windows.DevHome.SDK; -namespace AzureExtension.QuickStartPlayground; +namespace DevHomeAzureExtension.QuickStartPlayground; public sealed class DevContainerProjectHost : IQuickStartProjectHost { diff --git a/src/AzureExtension/QuickStartPlayground/DevContainerQuickStartProjectProvider.cs b/src/AzureExtension/QuickStartPlayground/DevContainerQuickStartProjectProvider.cs index 20a773b5..870f234d 100644 --- a/src/AzureExtension/QuickStartPlayground/DevContainerQuickStartProjectProvider.cs +++ b/src/AzureExtension/QuickStartPlayground/DevContainerQuickStartProjectProvider.cs @@ -1,12 +1,12 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using AzureExtension.Contracts; +using DevHomeAzureExtension.Contracts; using DevHomeAzureExtension.Helpers; using Microsoft.Windows.DevHome.SDK; using Windows.Storage; -namespace AzureExtension.QuickStartPlayground; +namespace DevHomeAzureExtension.QuickStartPlayground; public abstract class DevContainerQuickStartProjectProvider : IQuickStartProjectProvider { diff --git a/src/AzureExtension/QuickStartPlayground/DockerProgressUIController.cs b/src/AzureExtension/QuickStartPlayground/DockerProgressUIController.cs index 1f30e2c7..dbc70b04 100644 --- a/src/AzureExtension/QuickStartPlayground/DockerProgressUIController.cs +++ b/src/AzureExtension/QuickStartPlayground/DockerProgressUIController.cs @@ -8,7 +8,7 @@ using Microsoft.Windows.DevHome.SDK; using Windows.Foundation; -namespace AzureExtension.QuickStartPlayground; +namespace DevHomeAzureExtension.QuickStartPlayground; public sealed partial class DockerProgressUIController : IExtensionAdaptiveCardSession2 { diff --git a/src/AzureExtension/QuickStartPlayground/DockerValidation.cs b/src/AzureExtension/QuickStartPlayground/DockerValidation.cs index d2d2601e..bd27dc81 100644 --- a/src/AzureExtension/QuickStartPlayground/DockerValidation.cs +++ b/src/AzureExtension/QuickStartPlayground/DockerValidation.cs @@ -4,7 +4,7 @@ using System.Diagnostics; using Windows.Storage; -namespace AzureExtension.QuickStartPlayground; +namespace DevHomeAzureExtension.QuickStartPlayground; public static class DockerValidation { diff --git a/src/AzureExtension/QuickStartPlayground/EmbeddingsCalc.cs b/src/AzureExtension/QuickStartPlayground/EmbeddingsCalc.cs index 5f5317ae..2bcfc5c0 100644 --- a/src/AzureExtension/QuickStartPlayground/EmbeddingsCalc.cs +++ b/src/AzureExtension/QuickStartPlayground/EmbeddingsCalc.cs @@ -4,7 +4,7 @@ using System.Globalization; using System.Numerics.Tensors; -namespace AzureExtension.QuickStartPlayground; +namespace DevHomeAzureExtension.QuickStartPlayground; /// /// This class contains helper methods to perform vector database-like operations on the diff --git a/src/AzureExtension/QuickStartPlayground/ExtensionInitializationUIController.cs b/src/AzureExtension/QuickStartPlayground/ExtensionInitializationUIController.cs index 2483fd99..a7759c2b 100644 --- a/src/AzureExtension/QuickStartPlayground/ExtensionInitializationUIController.cs +++ b/src/AzureExtension/QuickStartPlayground/ExtensionInitializationUIController.cs @@ -4,7 +4,7 @@ using Microsoft.Windows.DevHome.SDK; using Windows.Foundation; -namespace AzureExtension.QuickStartPlayground; +namespace DevHomeAzureExtension.QuickStartPlayground; // This class serves as a wrapper around multiple adaptive cards which are displayed one after // another but don't know about each other. After each adaptive card is done, the next is displayed. diff --git a/src/AzureExtension/QuickStartPlayground/InstalledAppsService.cs b/src/AzureExtension/QuickStartPlayground/InstalledAppsService.cs index ca64fe84..03b370fb 100644 --- a/src/AzureExtension/QuickStartPlayground/InstalledAppsService.cs +++ b/src/AzureExtension/QuickStartPlayground/InstalledAppsService.cs @@ -2,12 +2,12 @@ // Licensed under the MIT License. using System.Diagnostics; -using AzureExtension.Contracts; +using DevHomeAzureExtension.Contracts; using Serilog; using Windows.Management.Deployment; using Windows.System.Inventory; -namespace AzureExtension.QuickStartPlayground; +namespace DevHomeAzureExtension.QuickStartPlayground; public class InstalledAppsService : IInstalledAppsService { diff --git a/src/AzureExtension/QuickStartPlayground/OpenAIDevContainerQuickStartProjectProvider.cs b/src/AzureExtension/QuickStartPlayground/OpenAIDevContainerQuickStartProjectProvider.cs index 887893d1..af4eaf91 100644 --- a/src/AzureExtension/QuickStartPlayground/OpenAIDevContainerQuickStartProjectProvider.cs +++ b/src/AzureExtension/QuickStartPlayground/OpenAIDevContainerQuickStartProjectProvider.cs @@ -4,12 +4,12 @@ using System.Security; using System.Text.Json; using System.Text.Json.Serialization; -using AzureExtension.Contracts; +using DevHomeAzureExtension.Contracts; using DevHomeAzureExtension.Helpers; using Microsoft.Windows.DevHome.SDK; using Windows.Foundation; -namespace AzureExtension.QuickStartPlayground; +namespace DevHomeAzureExtension.QuickStartPlayground; public sealed partial class OpenAIDevContainerQuickStartProjectProvider : DevContainerQuickStartProjectProvider { diff --git a/src/AzureExtension/QuickStartPlayground/ServiceExtensions.cs b/src/AzureExtension/QuickStartPlayground/ServiceExtensions.cs index 5aeeadb6..85e0c390 100644 --- a/src/AzureExtension/QuickStartPlayground/ServiceExtensions.cs +++ b/src/AzureExtension/QuickStartPlayground/ServiceExtensions.cs @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using AzureExtension.Contracts; +using DevHomeAzureExtension.Contracts; using Microsoft.Extensions.DependencyInjection; using Microsoft.Windows.DevHome.SDK; -namespace AzureExtension.QuickStartPlayground; +namespace DevHomeAzureExtension.QuickStartPlayground; public static class ServiceExtensions { diff --git a/src/AzureExtension/QuickStartPlayground/TrainingSample.cs b/src/AzureExtension/QuickStartPlayground/TrainingSample.cs index c9a6a121..6999e94c 100644 --- a/src/AzureExtension/QuickStartPlayground/TrainingSample.cs +++ b/src/AzureExtension/QuickStartPlayground/TrainingSample.cs @@ -3,7 +3,7 @@ using System.Text.Json.Serialization; -namespace AzureExtension.QuickStartPlayground; +namespace DevHomeAzureExtension.QuickStartPlayground; public sealed class TrainingSample { diff --git a/src/AzureExtension/Services/DevBox/ARMTokenService.cs b/src/AzureExtension/Services/DevBox/ARMTokenService.cs index c45b9e65..7bd81b2e 100644 --- a/src/AzureExtension/Services/DevBox/ARMTokenService.cs +++ b/src/AzureExtension/Services/DevBox/ARMTokenService.cs @@ -1,13 +1,13 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using AzureExtension.Contracts; -using AzureExtension.DevBox; +using DevHomeAzureExtension.Contracts; using DevHomeAzureExtension.DeveloperId; using Microsoft.Windows.DevHome.SDK; using Serilog; +using DevBoxConstants = DevHomeAzureExtension.DevBox.Constants; -namespace AzureExtension.Services.DevBox; +namespace DevHomeAzureExtension.Services.DevBox; /// /// Implementation of the Azure Resource Manager (ARM) token service. @@ -26,7 +26,7 @@ public async Task GetTokenAsync(IDeveloperId? devId) return string.Empty; } - var result = await AuthHelper.ObtainTokenForLoggedInDeveloperAccount(new string[] { Constants.ManagementPlaneScope }, devId.LoginId); + var result = await AuthHelper.ObtainTokenForLoggedInDeveloperAccount(new string[] { DevBoxConstants.ManagementPlaneScope }, devId.LoginId); return result?.AccessToken ?? string.Empty; } } diff --git a/src/AzureExtension/Services/DevBox/AuthService.cs b/src/AzureExtension/Services/DevBox/AuthService.cs index 5a619770..b067aa1e 100644 --- a/src/AzureExtension/Services/DevBox/AuthService.cs +++ b/src/AzureExtension/Services/DevBox/AuthService.cs @@ -2,11 +2,11 @@ // Licensed under the MIT License. using System.Net.Http.Headers; -using AzureExtension.Contracts; +using DevHomeAzureExtension.Contracts; using Microsoft.Windows.DevHome.SDK; using Windows.ApplicationModel; -namespace AzureExtension.Services.DevBox; +namespace DevHomeAzureExtension.Services.DevBox; public class AuthService : IDevBoxAuthService { diff --git a/src/AzureExtension/Services/DevBox/DataTokenService.cs b/src/AzureExtension/Services/DevBox/DataTokenService.cs index 84611789..cbecbc75 100644 --- a/src/AzureExtension/Services/DevBox/DataTokenService.cs +++ b/src/AzureExtension/Services/DevBox/DataTokenService.cs @@ -1,13 +1,13 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using AzureExtension.Contracts; -using AzureExtension.DevBox; +using DevHomeAzureExtension.Contracts; using DevHomeAzureExtension.DeveloperId; using Microsoft.Windows.DevHome.SDK; using Serilog; +using DevBoxConstants = DevHomeAzureExtension.DevBox.Constants; -namespace AzureExtension.Services.DevBox; +namespace DevHomeAzureExtension.Services.DevBox; /// /// Implementation of Dev Box Data Plane token service. @@ -28,7 +28,7 @@ public async Task GetTokenAsync(IDeveloperId? devId) try { - var result = await AuthHelper.ObtainTokenForLoggedInDeveloperAccount(new string[] { Constants.DataPlaneScope }, devId.LoginId); + var result = await AuthHelper.ObtainTokenForLoggedInDeveloperAccount(new string[] { DevBoxConstants.DataPlaneScope }, devId.LoginId); return result?.AccessToken ?? string.Empty; } catch (Exception e) diff --git a/src/AzureExtension/Services/DevBox/DevBoxCreationManager.cs b/src/AzureExtension/Services/DevBox/DevBoxCreationManager.cs index f30939e0..b833b99c 100644 --- a/src/AzureExtension/Services/DevBox/DevBoxCreationManager.cs +++ b/src/AzureExtension/Services/DevBox/DevBoxCreationManager.cs @@ -2,15 +2,16 @@ // Licensed under the MIT License. using System.Text.Json; -using AzureExtension.Contracts; -using AzureExtension.DevBox; -using AzureExtension.DevBox.DevBoxJsonToCsClasses; -using AzureExtension.DevBox.Models; +using DevHomeAzureExtension.Contracts; +using DevHomeAzureExtension.DevBox; +using DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; +using DevHomeAzureExtension.DevBox.Models; using DevHomeAzureExtension.Helpers; using Microsoft.Windows.DevHome.SDK; using Serilog; +using DevBoxConstants = DevHomeAzureExtension.DevBox.Constants; -namespace AzureExtension.Services.DevBox; +namespace DevHomeAzureExtension.Services.DevBox; public class DevBoxCreationManager : IDevBoxCreationManager { @@ -49,15 +50,15 @@ public async Task StartCreateDevBoxOperation(CreateCo try { _log.Information($"Starting the create DevBox operation for new environment with new: {parameters.NewEnvironmentName}"); - operation.UpdateProgress(Resources.GetResource(SendingCreationRequestProgressKey), Constants.IndefiniteProgress); + operation.UpdateProgress(Resources.GetResource(SendingCreationRequestProgressKey), DevBoxConstants.IndefiniteProgress); var result = await _devBoxManagementService.CreateDevBox(parameters, developerId); - operation.UpdateProgress(Resources.GetResource(CreationResponseReceivedProgressKey, parameters.NewEnvironmentName, parameters.ProjectName), Constants.IndefiniteProgress); + operation.UpdateProgress(Resources.GetResource(CreationResponseReceivedProgressKey, parameters.NewEnvironmentName, parameters.ProjectName), DevBoxConstants.IndefiniteProgress); - var devBoxState = JsonSerializer.Deserialize(result.JsonResponseRoot.ToString(), Constants.JsonOptions)!; + var devBoxState = JsonSerializer.Deserialize(result.JsonResponseRoot.ToString(), DevBoxConstants.JsonOptions)!; var devBox = _devBoxInstanceFactory(developerId, devBoxState); - operation.UpdateProgress(Resources.GetResource(DevCenterCreationStartedProgressKey, parameters.NewEnvironmentName, parameters.ProjectName), Constants.IndefiniteProgress); + operation.UpdateProgress(Resources.GetResource(DevCenterCreationStartedProgressKey, parameters.NewEnvironmentName, parameters.ProjectName), DevBoxConstants.IndefiniteProgress); var callback = DevCenterLongRunningOperationCallback(devBox); diff --git a/src/AzureExtension/Services/DevBox/DevBoxManagementService.cs b/src/AzureExtension/Services/DevBox/DevBoxManagementService.cs index c5311841..da4315ff 100644 --- a/src/AzureExtension/Services/DevBox/DevBoxManagementService.cs +++ b/src/AzureExtension/Services/DevBox/DevBoxManagementService.cs @@ -2,20 +2,19 @@ // Licensed under the MIT License. using System.Collections.Concurrent; -using System.Net; using System.Net.Http.Headers; using System.Text; using System.Text.Json; using System.Text.RegularExpressions; -using AzureExtension.Contracts; -using AzureExtension.DevBox; -using AzureExtension.DevBox.DevBoxJsonToCsClasses; -using AzureExtension.DevBox.Exceptions; -using AzureExtension.DevBox.Models; +using DevHomeAzureExtension.Contracts; +using DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; +using DevHomeAzureExtension.DevBox.Exceptions; +using DevHomeAzureExtension.DevBox.Models; using Microsoft.Windows.DevHome.SDK; using Serilog; +using DevBoxConstants = DevHomeAzureExtension.DevBox.Constants; -namespace AzureExtension.Services.DevBox; +namespace DevHomeAzureExtension.Services.DevBox; /// /// The DevBoxManagementService is responsible for making calls to the Azure Resource Graph API. @@ -141,9 +140,9 @@ await Parallel.ForEachAsync(projects.Data!, async (project, token) => try { var properties = project.Properties; - var uriToRetrievePools = $"{properties.DevCenterUri}{Constants.Projects}/{project.Name}/{Constants.Pools}?{Constants.APIVersion}"; + var uriToRetrievePools = $"{properties.DevCenterUri}{DevBoxConstants.Projects}/{project.Name}/{DevBoxConstants.Pools}?{DevBoxConstants.APIVersion}"; var result = await HttpsRequestToDataPlane(new Uri(uriToRetrievePools), developerId, HttpMethod.Get); - var pools = JsonSerializer.Deserialize(result.JsonResponseRoot.ToString(), Constants.JsonOptions); + var pools = JsonSerializer.Deserialize(result.JsonResponseRoot.ToString(), DevBoxConstants.JsonOptions); var container = new DevBoxProjectAndPoolContainer { Project = project, Pools = pools }; projectsToPoolsMapping.Add(container); @@ -161,17 +160,17 @@ await Parallel.ForEachAsync(projects.Data!, async (project, token) => /// public async Task CreateDevBox(DevBoxCreationParameters parameters, IDeveloperId developerId) { - if (!Regex.IsMatch(parameters.NewEnvironmentName, Constants.NameRegexPattern)) + if (!Regex.IsMatch(parameters.NewEnvironmentName, DevBoxConstants.NameRegexPattern)) { throw new DevBoxNameInvalidException($"Unable to create Dev Box due to Invalid Dev Box name: {parameters.NewEnvironmentName}"); } - if (!Regex.IsMatch(parameters.ProjectName, Constants.NameRegexPattern)) + if (!Regex.IsMatch(parameters.ProjectName, DevBoxConstants.NameRegexPattern)) { throw new DevBoxProjectNameInvalidException($"Unable to create Dev Box due to Invalid project name: {parameters.ProjectName}"); } - var uriToCreateDevBox = $"{parameters.DevCenterUri}{Constants.Projects}/{parameters.ProjectName}{Constants.DevBoxUserSegmentOfUri}/{parameters.NewEnvironmentName}?{Constants.APIVersion}"; + var uriToCreateDevBox = $"{parameters.DevCenterUri}{DevBoxConstants.Projects}/{parameters.ProjectName}{DevBoxConstants.DevBoxUserSegmentOfUri}/{parameters.NewEnvironmentName}?{DevBoxConstants.APIVersion}"; var contentJson = JsonSerializer.Serialize(new DevBoxCreationPoolName(parameters.PoolName)); var content = new StringContent(contentJson, Encoding.UTF8, "application/json"); return await HttpsRequestToDataPlane(new Uri(uriToCreateDevBox), developerId, HttpMethod.Put, content); diff --git a/src/AzureExtension/Services/DevBox/DevBoxOperationWatcher.cs b/src/AzureExtension/Services/DevBox/DevBoxOperationWatcher.cs index 2e0e1512..d6e3a822 100644 --- a/src/AzureExtension/Services/DevBox/DevBoxOperationWatcher.cs +++ b/src/AzureExtension/Services/DevBox/DevBoxOperationWatcher.cs @@ -2,16 +2,17 @@ // Licensed under the MIT License. using System.Text.Json; -using AzureExtension.Contracts; -using AzureExtension.DevBox; -using AzureExtension.DevBox.DevBoxJsonToCsClasses; -using AzureExtension.DevBox.Exceptions; -using AzureExtension.DevBox.Models; +using DevHomeAzureExtension.Contracts; +using DevHomeAzureExtension.DevBox; +using DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; +using DevHomeAzureExtension.DevBox.Exceptions; +using DevHomeAzureExtension.DevBox.Models; using Microsoft.Windows.DevHome.SDK; using Serilog; using Windows.System.Threading; +using DevBoxConstants = DevHomeAzureExtension.DevBox.Constants; -namespace AzureExtension.Services.DevBox; +namespace DevHomeAzureExtension.Services.DevBox; /// /// Represents the status of a Dev Box provisioning operation. @@ -76,7 +77,7 @@ public void StartDevCenterOperationMonitor(IDeveloperId developerId, Uri operati // Query the Dev Center for the status of the Dev Box operation. var result = await _managementService.HttpsRequestToDataPlane(operationUri, developerId, HttpMethod.Get, null); - var operation = JsonSerializer.Deserialize(result.JsonResponseRoot.ToString(), Constants.JsonOptions)!; + var operation = JsonSerializer.Deserialize(result.JsonResponseRoot.ToString(), DevBoxConstants.JsonOptions)!; // Invoke the call back so the original requester can handle the status update of the operation. completionCallback(operation.Status); @@ -137,12 +138,12 @@ public void StartDevBoxProvisioningStatusMonitor(IDeveloperId developerId, DevBo _log.Information($"Starting the provisioning monitor for Dev Box with Name: '{devBoxInstance.DisplayName}' , Id: '{devBoxInstance.Id}'"); // Query the Dev Center for the provisioning status of the Dev Box. This is needed for when the Dev Box was created outside of Dev Home. - var devBoxUri = $"{devBoxInstance.DevBoxState.Uri}?{Constants.APIVersion}"; + var devBoxUri = $"{devBoxInstance.DevBoxState.Uri}?{DevBoxConstants.APIVersion}"; var result = await _managementService.HttpsRequestToDataPlane(new Uri(devBoxUri), developerId, HttpMethod.Get, null); - var devBoxState = JsonSerializer.Deserialize(result.JsonResponseRoot.ToString(), Constants.JsonOptions)!; + var devBoxState = JsonSerializer.Deserialize(result.JsonResponseRoot.ToString(), DevBoxConstants.JsonOptions)!; // If the Dev Box is no longer being created, update the state for Dev Homes UI and end the timer. - if (!(devBoxState.ProvisioningState == Constants.DevBoxProvisioningStates.Creating || devBoxState.ProvisioningState == Constants.DevBoxProvisioningStates.Provisioning)) + if (!(devBoxState.ProvisioningState == DevBoxConstants.DevBoxProvisioningStates.Creating || devBoxState.ProvisioningState == DevBoxConstants.DevBoxProvisioningStates.Provisioning)) { _log.Information($"Dev Box provisioning now completed."); devBoxInstance.ProvisioningMonitorCompleted(devBoxState, ProvisioningStatus.Succeeded); diff --git a/src/AzureExtension/Services/DevBox/PackagesService.cs b/src/AzureExtension/Services/DevBox/PackagesService.cs index 1bb3f1a5..b623c4e1 100644 --- a/src/AzureExtension/Services/DevBox/PackagesService.cs +++ b/src/AzureExtension/Services/DevBox/PackagesService.cs @@ -1,10 +1,10 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using AzureExtension.Contracts; +using DevHomeAzureExtension.Contracts; using Windows.ApplicationModel; -namespace AzureExtension.Services.DevBox; +namespace DevHomeAzureExtension.Services.DevBox; public class PackagesService : IPackagesService { diff --git a/src/AzureExtension/Services/DevBox/TimeSpanService.cs b/src/AzureExtension/Services/DevBox/TimeSpanService.cs index 8af58701..9aa252f4 100644 --- a/src/AzureExtension/Services/DevBox/TimeSpanService.cs +++ b/src/AzureExtension/Services/DevBox/TimeSpanService.cs @@ -1,17 +1,18 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using AzureExtension.Contracts; -using AzureExtension.DevBox; +using DevHomeAzureExtension.Contracts; +using DevHomeAzureExtension.DevBox; +using DevBoxConstants = DevHomeAzureExtension.DevBox.Constants; -namespace AzureExtension.Services.DevBox; +namespace DevHomeAzureExtension.Services.DevBox; /// /// Service to provide time spans for different operations. /// public class TimeSpanService : ITimeSpanService { - public TimeSpan DevBoxOperationDeadline { get; private set; } = Constants.OperationDeadline; + public TimeSpan DevBoxOperationDeadline { get; private set; } = DevBoxConstants.OperationDeadline; /// /// Get the period interval based on the action to perform. @@ -28,9 +29,9 @@ public TimeSpan GetPeriodIntervalBasedOnAction(DevBoxActionToPerform actionToPer switch (actionToPerform) { case DevBoxActionToPerform.Create: - return Constants.ThreeMinutePeriod; + return DevBoxConstants.ThreeMinutePeriod; default: - return Constants.OneMinutePeriod; + return DevBoxConstants.OneMinutePeriod; } } } diff --git a/src/AzureExtensionServer/Program.cs b/src/AzureExtensionServer/Program.cs index a26c936d..5774124d 100644 --- a/src/AzureExtensionServer/Program.cs +++ b/src/AzureExtensionServer/Program.cs @@ -1,14 +1,14 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using AzureExtension.Contracts; -using AzureExtension.DevBox; -using AzureExtension.DevBox.Models; -using AzureExtension.Providers; -using AzureExtension.QuickStartPlayground; -using AzureExtension.Services.DevBox; +using DevHomeAzureExtension.Contracts; using DevHomeAzureExtension.DataModel; +using DevHomeAzureExtension.DevBox; +using DevHomeAzureExtension.DevBox.Models; using DevHomeAzureExtension.DeveloperId; +using DevHomeAzureExtension.Providers; +using DevHomeAzureExtension.QuickStartPlayground; +using DevHomeAzureExtension.Services.DevBox; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; diff --git a/test/AzureExtension/DevBox/ArmTestTokenService.cs b/test/AzureExtension/DevBox/ArmTestTokenService.cs index 4a3f2e66..0a352d3a 100644 --- a/test/AzureExtension/DevBox/ArmTestTokenService.cs +++ b/test/AzureExtension/DevBox/ArmTestTokenService.cs @@ -1,10 +1,10 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using AzureExtension.Contracts; +using DevHomeAzureExtension.Contracts; using Microsoft.Windows.DevHome.SDK; -namespace AzureExtension.Test.DevBox; +namespace DevHomeAzureExtension.Test.DevBox; public class ArmTestTokenService : IArmTokenService { diff --git a/test/AzureExtension/DevBox/DataTestTokenService.cs b/test/AzureExtension/DevBox/DataTestTokenService.cs index a387b6ae..4b44ad31 100644 --- a/test/AzureExtension/DevBox/DataTestTokenService.cs +++ b/test/AzureExtension/DevBox/DataTestTokenService.cs @@ -1,10 +1,10 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using AzureExtension.Contracts; +using DevHomeAzureExtension.Contracts; using Microsoft.Windows.DevHome.SDK; -namespace AzureExtension.Test.DevBox; +namespace DevHomeAzureExtension.Test.DevBox; public class DataTestTokenService : IDataTokenService { diff --git a/test/AzureExtension/DevBox/DevBoxTests.cs b/test/AzureExtension/DevBox/DevBoxTests.cs index 2f7cee35..de084adb 100644 --- a/test/AzureExtension/DevBox/DevBoxTests.cs +++ b/test/AzureExtension/DevBox/DevBoxTests.cs @@ -2,10 +2,10 @@ // Licensed under the MIT License. using System.Text.Json; -using AzureExtension.DevBox; -using AzureExtension.DevBox.DevBoxJsonToCsClasses; -using AzureExtension.DevBox.Models; -using AzureExtension.Test.DevBox; +using DevHomeAzureExtension.DevBox; +using DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; +using DevHomeAzureExtension.DevBox.Models; +using DevHomeAzureExtension.Test.DevBox; using Microsoft.Extensions.DependencyInjection; using Microsoft.Windows.DevHome.SDK; diff --git a/test/AzureExtension/DevBox/DevBoxTestsSetup.cs b/test/AzureExtension/DevBox/DevBoxTestsSetup.cs index 438ee1c3..26a8f8d3 100644 --- a/test/AzureExtension/DevBox/DevBoxTestsSetup.cs +++ b/test/AzureExtension/DevBox/DevBoxTestsSetup.cs @@ -3,11 +3,11 @@ using System.Net; using System.Net.Http.Headers; -using AzureExtension.Contracts; -using AzureExtension.DevBox; -using AzureExtension.DevBox.Models; -using AzureExtension.Services.DevBox; -using AzureExtension.Test.DevBox; +using DevHomeAzureExtension.Contracts; +using DevHomeAzureExtension.DevBox; +using DevHomeAzureExtension.DevBox.Models; +using DevHomeAzureExtension.Services.DevBox; +using DevHomeAzureExtension.Test.DevBox; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Moq; diff --git a/test/AzureExtension/DevBox/EmptyDeveloperId.cs b/test/AzureExtension/DevBox/EmptyDeveloperId.cs index a842d0e2..7dab4f79 100644 --- a/test/AzureExtension/DevBox/EmptyDeveloperId.cs +++ b/test/AzureExtension/DevBox/EmptyDeveloperId.cs @@ -3,7 +3,7 @@ using Microsoft.Windows.DevHome.SDK; -namespace AzureExtension.Test.DevBox; +namespace DevHomeAzureExtension.Test.DevBox; public class EmptyDeveloperId : IDeveloperId { diff --git a/test/AzureExtension/DevBox/TimeSpanServiceMock.cs b/test/AzureExtension/DevBox/TimeSpanServiceMock.cs index f7a11b0e..014daac0 100644 --- a/test/AzureExtension/DevBox/TimeSpanServiceMock.cs +++ b/test/AzureExtension/DevBox/TimeSpanServiceMock.cs @@ -1,10 +1,10 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using AzureExtension.Contracts; -using AzureExtension.DevBox; +using DevHomeAzureExtension.Contracts; +using DevHomeAzureExtension.DevBox; -namespace AzureExtension.Test.DevBox; +namespace DevHomeAzureExtension.Test.DevBox; public class TimeSpanServiceMock : ITimeSpanService { diff --git a/test/AzureExtension/DeveloperId/Mocks/MockAuthenticationSettings.cs b/test/AzureExtension/DeveloperId/Mocks/MockAuthenticationSettings.cs index f1fc663c..e35f494a 100644 --- a/test/AzureExtension/DeveloperId/Mocks/MockAuthenticationSettings.cs +++ b/test/AzureExtension/DeveloperId/Mocks/MockAuthenticationSettings.cs @@ -3,7 +3,7 @@ using DevHomeAzureExtension.DeveloperId; -namespace AzureExtension.Test.DeveloperId.Mocks; +namespace DevHomeAzureExtension.Test.DeveloperId.Mocks; public class MockAuthenticationSettings : IAuthenticationSettings { From d0b1f3656b3671ad4f85b5874c7da3cabb0ad6b6 Mon Sep 17 00:00:00 2001 From: Kristen Schau <47155823+krschau@users.noreply.github.com> Date: Wed, 26 Jun 2024 14:46:49 -0400 Subject: [PATCH 04/11] Update log names (#231) --- exclusion.dic | 3 + .../Client/AzureClientProvider.cs | 56 +++++++++---------- src/AzureExtension/Client/AzureUri.cs | 24 ++++---- .../Contracts/IAzureOpenAIService.cs | 22 ++++---- .../Contracts/IDevBoxManagementService.cs | 2 +- .../DataModel/DataObjects/Identity.cs | 14 ++--- .../DataModel/DataObjects/Project.cs | 6 +- .../DataModel/DataObjects/PullRequests.cs | 8 +-- .../DataModel/DataObjects/Query.cs | 8 +-- .../DataModel/DataObjects/WorkItemType.cs | 10 ++-- .../DeveloperId/AuthenticationHelper.cs | 56 +++++++++---------- .../Helpers/AzureRepositoryHierarchy.cs | 6 +- .../Notifications/NotificationHandler.cs | 10 ++-- .../Providers/SettingsUIController.cs | 10 ++-- 14 files changed, 119 insertions(+), 116 deletions(-) diff --git a/exclusion.dic b/exclusion.dic index 03059a26..e71be4d6 100644 --- a/exclusion.dic +++ b/exclusion.dic @@ -23,3 +23,6 @@ codespace codespaces dhlog appsettings +deserializer +winget +csharp diff --git a/src/AzureExtension/Client/AzureClientProvider.cs b/src/AzureExtension/Client/AzureClientProvider.cs index 8c59fa87..18a97cd5 100644 --- a/src/AzureExtension/Client/AzureClientProvider.cs +++ b/src/AzureExtension/Client/AzureClientProvider.cs @@ -10,22 +10,22 @@ namespace DevHomeAzureExtension.Client; public class AzureClientProvider { - private static readonly Lazy _log = new(() => Serilog.Log.ForContext("SourceContext", nameof(AzureClientProvider))); + private static readonly Lazy _logger = new(() => Serilog.Log.ForContext("SourceContext", nameof(AzureClientProvider))); - private static readonly ILogger Log = _log.Value; + private static readonly ILogger _log = _logger.Value; private static VssConnection? CreateConnection(Uri uri, DeveloperId.DeveloperId developerId) { var azureUri = new AzureUri(uri); if (!azureUri.IsValid) { - Log.Information($"Cannot Create Connection: invalid uri argument value i.e. {uri}"); + _log.Information($"Cannot Create Connection: invalid uri argument value i.e. {uri}"); return null; } if (developerId == null) { - Log.Information($"Cannot Create Connection: null developer id argument"); + _log.Information($"Cannot Create Connection: null developer id argument"); return null; } @@ -38,41 +38,41 @@ public class AzureClientProvider if (credentials == null) { - Log.Error($"Unable to get credentials for developerId"); + _log.Error($"Unable to get credentials for developerId"); return null; } } catch (MsalUiRequiredException ex) { - Log.Error($"Unable to get credentials for developerId failed and requires user interaction {ex}"); + _log.Error($"Unable to get credentials for developerId failed and requires user interaction {ex}"); return null; } catch (MsalServiceException ex) { - Log.Error($"Unable to get credentials for developerId: failed with MSAL service error: {ex}"); + _log.Error($"Unable to get credentials for developerId: failed with MSAL service error: {ex}"); return null; } catch (MsalClientException ex) { - Log.Error($"Unable to get credentials for developerId: failed with MSAL client error: {ex}"); + _log.Error($"Unable to get credentials for developerId: failed with MSAL client error: {ex}"); return null; } catch (Exception ex) { - Log.Error($"Unable to get credentials for developerId {ex}"); + _log.Error($"Unable to get credentials for developerId {ex}"); return null; } var connection = new VssConnection(azureUri.Connection, credentials); if (connection != null) { - Log.Debug($"Connection created for developer id"); + _log.Debug($"Connection created for developer id"); return connection; } } catch (Exception ex) { - Log.Error(ex, $"Failed creating connection for developer id and {uri} with exception:"); + _log.Error(ex, $"Failed creating connection for developer id and {uri} with exception:"); } return null; @@ -83,13 +83,13 @@ public static ConnectionResult CreateVssConnection(Uri uri, DeveloperId.Develope var azureUri = new AzureUri(uri); if (!azureUri.IsValid) { - Log.Information($"Cannot Create Connection: invalid uri argument value i.e. {uri}"); + _log.Information($"Cannot Create Connection: invalid uri argument value i.e. {uri}"); return new ConnectionResult(ResultType.Failure, ErrorType.InvalidArgument, false); } if (developerId == null) { - Log.Information($"Cannot Create Connection: invalid developer id argument"); + _log.Information($"Cannot Create Connection: invalid developer id argument"); return new ConnectionResult(ResultType.Failure, ErrorType.InvalidArgument, false); } @@ -99,28 +99,28 @@ public static ConnectionResult CreateVssConnection(Uri uri, DeveloperId.Develope credentials = developerId.GetCredentials(); if (credentials == null) { - Log.Error($"Unable to get credentials for developerId"); + _log.Error($"Unable to get credentials for developerId"); return new ConnectionResult(ResultType.Failure, ErrorType.InvalidDeveloperId, false); } } catch (MsalUiRequiredException ex) { - Log.Error($"AcquireDeveloperAccountToken failed and requires user interaction {ex}"); + _log.Error($"AcquireDeveloperAccountToken failed and requires user interaction {ex}"); return new ConnectionResult(ResultType.Failure, ErrorType.CredentialUIRequired, false, ex); } catch (MsalServiceException ex) { - Log.Error($"AcquireDeveloperAccountToken failed with MSAL service error: {ex}"); + _log.Error($"AcquireDeveloperAccountToken failed with MSAL service error: {ex}"); return new ConnectionResult(ResultType.Failure, ErrorType.MsalServiceError, false, ex); } catch (MsalClientException ex) { - Log.Error($"AcquireDeveloperAccountToken failed with MSAL client error: {ex}"); + _log.Error($"AcquireDeveloperAccountToken failed with MSAL client error: {ex}"); return new ConnectionResult(ResultType.Failure, ErrorType.MsalClientError, false, ex); } catch (Exception ex) { - Log.Error($"AcquireDeveloperAccountToken failed with error: {ex}"); + _log.Error($"AcquireDeveloperAccountToken failed with error: {ex}"); return new ConnectionResult(ResultType.Failure, ErrorType.GenericCredentialFailure, true); } @@ -134,18 +134,18 @@ public static ConnectionResult CreateVssConnection(Uri uri, DeveloperId.Develope if (connection != null) { - Log.Debug($"Created new connection to {azureUri.Connection} for {developerId.LoginId}"); + _log.Debug($"Created new connection to {azureUri.Connection} for {developerId.LoginId}"); return new ConnectionResult(azureUri.Connection, null, connection); } else { - Log.Error($"Connection to {azureUri.Connection} was null."); + _log.Error($"Connection to {azureUri.Connection} was null."); return new ConnectionResult(ResultType.Failure, ErrorType.NullConnection, false); } } catch (Exception ex) { - Log.Error($"Unable to establish VssConnection: {ex}"); + _log.Error($"Unable to establish VssConnection: {ex}"); return new ConnectionResult(ResultType.Failure, ErrorType.InitializeVssConnectionFailure, true, ex); } } @@ -164,20 +164,20 @@ public static VssConnection GetConnectionForLoggedInDeveloper(Uri uri, Developer var azureUri = new AzureUri(uri); if (!azureUri.IsValid) { - Log.Error($"Uri is an invalid Azure Uri: {uri}"); + _log.Error($"Uri is an invalid Azure Uri: {uri}"); throw new ArgumentException(uri.ToString()); } if (developerId == null) { - Log.Error($"No logged in developer for which connection needs to be retrieved"); + _log.Error($"No logged in developer for which connection needs to be retrieved"); throw new ArgumentNullException(null); } var connection = CreateConnection(azureUri.Connection, developerId); if (connection == null) { - Log.Error($"Failed creating connection for developer id"); + _log.Error($"Failed creating connection for developer id"); throw new AzureClientException($"Failed creating Vss connection: {azureUri.Connection} for {developerId.LoginId}"); } @@ -188,7 +188,7 @@ public static ConnectionResult GetVssConnectionForLoggedInDeveloper(Uri uri, Dev { if (developerId == null) { - Log.Error($"No logged in developer for which connection needs to be retrieved"); + _log.Error($"No logged in developer for which connection needs to be retrieved"); return new ConnectionResult(ResultType.Failure, ErrorType.InvalidDeveloperId, false); } @@ -200,14 +200,14 @@ public static ConnectionResult GetVssConnectionForLoggedInDeveloper(Uri uri, Dev { if (string.IsNullOrEmpty(uri)) { - Log.Information($"Cannot GetClient: invalid uri argument value i.e. {uri}"); + _log.Information($"Cannot GetClient: invalid uri argument value i.e. {uri}"); return null; } var azureUri = new AzureUri(uri); if (!azureUri.IsValid) { - Log.Information($"Cannot GetClient as uri validation failed: value of uri {uri}"); + _log.Information($"Cannot GetClient as uri validation failed: value of uri {uri}"); return null; } @@ -232,7 +232,7 @@ public static ConnectionResult GetAzureDevOpsClient(string uri, DeveloperId.D var azureUri = new AzureUri(uri); if (!azureUri.IsValid) { - Log.Information($"Cannot GetClient as uri validation failed: value of uri {uri}"); + _log.Information($"Cannot GetClient as uri validation failed: value of uri {uri}"); return new ConnectionResult(ResultType.Failure, ErrorType.InvalidArgument, false); } diff --git a/src/AzureExtension/Client/AzureUri.cs b/src/AzureExtension/Client/AzureUri.cs index 7be4f92e..4eccc74a 100644 --- a/src/AzureExtension/Client/AzureUri.cs +++ b/src/AzureExtension/Client/AzureUri.cs @@ -47,9 +47,9 @@ public class AzureUri /// private readonly Lazy _organizationLink; - private static readonly Lazy _log = new(() => Serilog.Log.ForContext("SourceContext", nameof(AzureUri))); + private static readonly Lazy _logger = new(() => Serilog.Log.ForContext("SourceContext", nameof(AzureUri))); - private static readonly ILogger Log = _log.Value; + private static readonly ILogger _log = _logger.Value; // Original input string, similar to Uri, but in cases of an invalid Uri they will be different. public string OriginalString { get; } = string.Empty; @@ -256,7 +256,7 @@ private string InitializeOrganization() } catch (Exception e) { - Log.Error(e, $"InitializeOrganization failed for Uri: {Uri}"); + _log.Error(e, $"InitializeOrganization failed for Uri: {Uri}"); return string.Empty; } } @@ -340,7 +340,7 @@ private string InitializeProject() } catch (Exception e) { - Log.Error(e, $"InitializeProject failed for Uri: {Uri}"); + _log.Error(e, $"InitializeProject failed for Uri: {Uri}"); return string.Empty; } } @@ -387,7 +387,7 @@ private string InitializeRepository() } catch (Exception e) { - Log.Error(e, $"InitializeRepository failed for Uri: {Uri}"); + _log.Error(e, $"InitializeRepository failed for Uri: {Uri}"); return string.Empty; } } @@ -453,7 +453,7 @@ private string InitializeQuery() } catch (Exception e) { - Log.Error(e, $"InitializeQuery failed for Uri: {Uri}"); + _log.Error(e, $"InitializeQuery failed for Uri: {Uri}"); return string.Empty; } } @@ -473,7 +473,7 @@ private Uri InitializeConnection() legacyUriString = legacyUriString.TrimEnd('/') + '/'; if (!Uri.TryCreate(legacyUriString, UriKind.Absolute, out newUri)) { - Log.Error($"Failed creating legacy Uri: {Uri} UriString: {legacyUriString}"); + _log.Error($"Failed creating legacy Uri: {Uri} UriString: {legacyUriString}"); } break; @@ -485,7 +485,7 @@ private Uri InitializeConnection() modernUriString = modernUriString.TrimEnd('/') + '/'; if (!Uri.TryCreate(modernUriString, UriKind.Absolute, out newUri)) { - Log.Error($"Failed creating modern Uri: {Uri} UriString: {modernUriString}"); + _log.Error($"Failed creating modern Uri: {Uri} UriString: {modernUriString}"); } break; @@ -496,7 +496,7 @@ private Uri InitializeConnection() onpremUriString = onpremUriString.TrimEnd('/') + '/'; if (!Uri.TryCreate(onpremUriString, UriKind.Absolute, out newUri)) { - Log.Error($"Failed creating On-Prem Uri: {Uri} UriString: {onpremUriString}"); + _log.Error($"Failed creating On-Prem Uri: {Uri} UriString: {onpremUriString}"); } break; @@ -529,7 +529,7 @@ private Uri InitializeOrganizationLink() var legacyOrgUri = Uri.Scheme + "://" + Uri.Host; if (!Uri.TryCreate(legacyOrgUri, UriKind.Absolute, out orgUri)) { - Log.Error("Could not make Org Uri"); + _log.Error("Could not make Org Uri"); } break; @@ -541,7 +541,7 @@ private Uri InitializeOrganizationLink() var modernOrgUri = Uri.Scheme + "://" + Uri.Host + "/" + Organization; if (!Uri.TryCreate(modernOrgUri, UriKind.Absolute, out orgUri)) { - Log.Error("Could not make Org Uri"); + _log.Error("Could not make Org Uri"); } break; @@ -550,7 +550,7 @@ private Uri InitializeOrganizationLink() var onpremOrgUri = Uri.Scheme + "://" + Uri.Host; if (!Uri.TryCreate(onpremOrgUri, UriKind.Absolute, out orgUri)) { - Log.Error("Could not make Org Uri"); + _log.Error("Could not make Org Uri"); } break; diff --git a/src/AzureExtension/Contracts/IAzureOpenAIService.cs b/src/AzureExtension/Contracts/IAzureOpenAIService.cs index 30c5ece9..84f936e7 100644 --- a/src/AzureExtension/Contracts/IAzureOpenAIService.cs +++ b/src/AzureExtension/Contracts/IAzureOpenAIService.cs @@ -1,7 +1,7 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -namespace DevHomeAzureExtension.Contracts; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace DevHomeAzureExtension.Contracts; public enum OpenAIEndpoint { @@ -10,16 +10,16 @@ public enum OpenAIEndpoint } public delegate IAzureOpenAIService AzureOpenAIServiceFactory(OpenAIEndpoint endpoint); - -public interface IAzureOpenAIService + +public interface IAzureOpenAIService { public IAICredentialService AICredentialService { get; } public void InitializeAIClient(); - public Uri GetEmbeddingsFile(); - - public ReadOnlyMemory GetEmbedding(string inputText); - + public Uri GetEmbeddingsFile(); + + public ReadOnlyMemory GetEmbedding(string inputText); + public Task GetAICompletionAsync(string systemInstructions, string userMessage); -} +} diff --git a/src/AzureExtension/Contracts/IDevBoxManagementService.cs b/src/AzureExtension/Contracts/IDevBoxManagementService.cs index 351e1894..e07beb11 100644 --- a/src/AzureExtension/Contracts/IDevBoxManagementService.cs +++ b/src/AzureExtension/Contracts/IDevBoxManagementService.cs @@ -42,7 +42,7 @@ public interface IDevBoxManagementService /// /// Generates a list of objects that each contain a Dev Center project and the Dev Box pools associated with that project. /// - /// The Deserialized Json recieved from a rest api that returns a list of Dev Center projects. + /// The Deserialized Json received from a rest api that returns a list of Dev Center projects. /// The DeveloperId associated with the request. /// A list of objects where each contain a project and its associated pools. public Task> GetAllProjectsToPoolsMappingAsync(DevBoxProjects projects, IDeveloperId developerId); diff --git a/src/AzureExtension/DataModel/DataObjects/Identity.cs b/src/AzureExtension/DataModel/DataObjects/Identity.cs index f7ab739b..e61e10e5 100644 --- a/src/AzureExtension/DataModel/DataObjects/Identity.cs +++ b/src/AzureExtension/DataModel/DataObjects/Identity.cs @@ -16,9 +16,9 @@ namespace DevHomeAzureExtension.DataModel; [Table("Identity")] public class Identity { - private static readonly Lazy _log = new(() => Serilog.Log.ForContext("SourceContext", $"DataModel/{nameof(Identity)}")); + private static readonly Lazy _logger = new(() => Serilog.Log.ForContext("SourceContext", $"DataModel/{nameof(Identity)}")); - private static readonly ILogger Log = _log.Value; + private static readonly ILogger _log = _logger.Value; // This is the time between seeing a potential updated Identity record and updating it. // This value / 2 is the average time between Identity updating their Identity data and having @@ -60,12 +60,12 @@ public static string GetAvatar(VssConnection connection, Guid identity) { var client = connection.GetClient(); var avatar = client.GetAvatarAsync(identity, AvatarSize.Small).Result; - Log.Debug($"Avatar found: {avatar.Value.Length} bytes."); + _log.Debug($"Avatar found: {avatar.Value.Length} bytes."); return Convert.ToBase64String(avatar.Value); } catch (Exception ex) { - Log.Warning(ex, $"Failed getting Avatar for {identity}."); + _log.Warning(ex, $"Failed getting Avatar for {identity}."); return string.Empty; } } @@ -90,7 +90,7 @@ public static string GetAvatar(VssConnection connection, Guid identity) } catch (Exception ex) { - Log.Error(ex, $"Failed to deserialize Json object into Identity: {json}"); + _log.Error(ex, $"Failed to deserialize Json object into Identity: {json}"); return null; } } @@ -198,8 +198,8 @@ public static void DeleteBefore(DataStore dataStore, DateTime date) var command = dataStore.Connection!.CreateCommand(); command.CommandText = sql; command.Parameters.AddWithValue("$Time", date.ToDataStoreInteger()); - Log.Debug(DataStore.GetCommandLogMessage(sql, command)); + _log.Debug(DataStore.GetCommandLogMessage(sql, command)); var rowsDeleted = command.ExecuteNonQuery(); - Log.Debug(DataStore.GetDeletedLogMessage(rowsDeleted)); + _log.Debug(DataStore.GetDeletedLogMessage(rowsDeleted)); } } diff --git a/src/AzureExtension/DataModel/DataObjects/Project.cs b/src/AzureExtension/DataModel/DataObjects/Project.cs index f38e069b..01fc24ad 100644 --- a/src/AzureExtension/DataModel/DataObjects/Project.cs +++ b/src/AzureExtension/DataModel/DataObjects/Project.cs @@ -12,9 +12,9 @@ namespace DevHomeAzureExtension.DataModel; [Table("Project")] public class Project { - private static readonly Lazy _log = new(() => Serilog.Log.ForContext("SourceContext", $"DataModel/{nameof(Project)}")); + private static readonly Lazy _logger = new(() => Serilog.Log.ForContext("SourceContext", $"DataModel/{nameof(Project)}")); - private static readonly ILogger Log = _log.Value; + private static readonly ILogger _log = _logger.Value; // This is the time between seeing a potential updated Project record and updating it. // This value / 2 is the average time between Project updating their Project data and having @@ -130,7 +130,7 @@ public static Project Get(DataStore? dataStore, long id) Org = organizationName, }; - Log.Debug(DataStore.GetSqlLogMessage(sql, param)); + _log.Debug(DataStore.GetSqlLogMessage(sql, param)); var project = dataStore.Connection!.QueryFirstOrDefault(sql, param, null); if (project is not null) { diff --git a/src/AzureExtension/DataModel/DataObjects/PullRequests.cs b/src/AzureExtension/DataModel/DataObjects/PullRequests.cs index 22432861..968b74e8 100644 --- a/src/AzureExtension/DataModel/DataObjects/PullRequests.cs +++ b/src/AzureExtension/DataModel/DataObjects/PullRequests.cs @@ -12,9 +12,9 @@ namespace DevHomeAzureExtension.DataModel; [Table("PullRequests")] public class PullRequests { - private static readonly Lazy _log = new(() => Serilog.Log.ForContext("SourceContext", $"DataModel/{nameof(PullRequests)}")); + private static readonly Lazy _logger = new(() => Serilog.Log.ForContext("SourceContext", $"DataModel/{nameof(PullRequests)}")); - private static readonly ILogger Log = _log.Value; + private static readonly ILogger _log = _logger.Value; // This is the time between seeing a search and updating it's TimeUpdated. private static readonly long _updateThreshold = TimeSpan.FromMinutes(2).Ticks; @@ -156,8 +156,8 @@ public static void DeleteBefore(DataStore dataStore, DateTime date) var command = dataStore.Connection!.CreateCommand(); command.CommandText = sql; command.Parameters.AddWithValue("$Time", date.ToDataStoreInteger()); - Log.Debug(DataStore.GetCommandLogMessage(sql, command)); + _log.Debug(DataStore.GetCommandLogMessage(sql, command)); var rowsDeleted = command.ExecuteNonQuery(); - Log.Debug(DataStore.GetDeletedLogMessage(rowsDeleted)); + _log.Debug(DataStore.GetDeletedLogMessage(rowsDeleted)); } } diff --git a/src/AzureExtension/DataModel/DataObjects/Query.cs b/src/AzureExtension/DataModel/DataObjects/Query.cs index 4c2befd1..9ffe5307 100644 --- a/src/AzureExtension/DataModel/DataObjects/Query.cs +++ b/src/AzureExtension/DataModel/DataObjects/Query.cs @@ -12,9 +12,9 @@ namespace DevHomeAzureExtension.DataModel; [Table("Query")] public class Query { - private static readonly Lazy _log = new(() => Serilog.Log.ForContext("SourceContext", $"DataModel/{nameof(Query)}")); + private static readonly Lazy _logger = new(() => Serilog.Log.ForContext("SourceContext", $"DataModel/{nameof(Query)}")); - private static readonly ILogger Log = _log.Value; + private static readonly ILogger _log = _logger.Value; // This is the time between seeing a search and updating it's TimeUpdated. private static readonly long _updateThreshold = TimeSpan.FromMinutes(2).Ticks; @@ -143,8 +143,8 @@ public static void DeleteBefore(DataStore dataStore, DateTime date) var command = dataStore.Connection!.CreateCommand(); command.CommandText = sql; command.Parameters.AddWithValue("$Time", date.ToDataStoreInteger()); - Log.Debug(DataStore.GetCommandLogMessage(sql, command)); + _log.Debug(DataStore.GetCommandLogMessage(sql, command)); var rowsDeleted = command.ExecuteNonQuery(); - Log.Debug(DataStore.GetDeletedLogMessage(rowsDeleted)); + _log.Debug(DataStore.GetDeletedLogMessage(rowsDeleted)); } } diff --git a/src/AzureExtension/DataModel/DataObjects/WorkItemType.cs b/src/AzureExtension/DataModel/DataObjects/WorkItemType.cs index fa560e3d..a34083f6 100644 --- a/src/AzureExtension/DataModel/DataObjects/WorkItemType.cs +++ b/src/AzureExtension/DataModel/DataObjects/WorkItemType.cs @@ -17,9 +17,9 @@ namespace DevHomeAzureExtension.DataModel; [Table("WorkItemType")] public class WorkItemType { - private static readonly Lazy _log = new(() => Serilog.Log.ForContext("SourceContext", $"DataModel/{nameof(WorkItemType)}")); + private static readonly Lazy _logger = new(() => Serilog.Log.ForContext("SourceContext", $"DataModel/{nameof(WorkItemType)}")); - private static readonly ILogger Log = _log.Value; + private static readonly ILogger _log = _logger.Value; // This is the time between seeing a potential updated WorkItemType record and updating it. // This value / 2 is the average time between WorkItemType updating their WorkItemType data @@ -82,7 +82,7 @@ public class WorkItemType } catch (Exception ex) { - Log.Error(ex, $"Failed to deserialize Json object into WorkItemType: {json}"); + _log.Error(ex, $"Failed to deserialize Json object into WorkItemType: {json}"); return null; } } @@ -184,8 +184,8 @@ public static void DeleteBefore(DataStore dataStore, DateTime date) var command = dataStore.Connection!.CreateCommand(); command.CommandText = sql; command.Parameters.AddWithValue("$Time", date.ToDataStoreInteger()); - Log.Debug(DataStore.GetCommandLogMessage(sql, command)); + _log.Debug(DataStore.GetCommandLogMessage(sql, command)); var rowsDeleted = command.ExecuteNonQuery(); - Log.Debug(DataStore.GetDeletedLogMessage(rowsDeleted)); + _log.Debug(DataStore.GetDeletedLogMessage(rowsDeleted)); } } diff --git a/src/AzureExtension/DeveloperId/AuthenticationHelper.cs b/src/AzureExtension/DeveloperId/AuthenticationHelper.cs index f420e824..e2895a44 100644 --- a/src/AzureExtension/DeveloperId/AuthenticationHelper.cs +++ b/src/AzureExtension/DeveloperId/AuthenticationHelper.cs @@ -38,9 +38,9 @@ public AuthenticationResult? AuthenticationResult private static readonly string[] _capabilities = ["cp1"]; - private static readonly Lazy _log = new(() => Serilog.Log.ForContext("SourceContext", nameof(AuthenticationHelper))); + private static readonly Lazy _logger = new(() => Serilog.Log.ForContext("SourceContext", nameof(AuthenticationHelper))); - private static readonly ILogger Log = _log.Value; + private static readonly ILogger _log = _logger.Value; public AuthenticationHelper() { @@ -54,14 +54,14 @@ public AuthenticationHelper() public void InitializePublicClientApplicationBuilder() { MicrosoftEntraIdSettings.InitializeSettings(); - Log.Debug($"Initialized MicrosoftEntraIdSettings"); + _log.Debug($"Initialized MicrosoftEntraIdSettings"); PublicClientApplicationBuilder = PublicClientApplicationBuilder.Create(MicrosoftEntraIdSettings.ClientId) .WithAuthority(string.Format(CultureInfo.InvariantCulture, MicrosoftEntraIdSettings.Authority, MicrosoftEntraIdSettings.TenantId)) .WithRedirectUri(string.Format(CultureInfo.InvariantCulture, MicrosoftEntraIdSettings.RedirectURI, MicrosoftEntraIdSettings.ClientId)) .WithLogging(new MSALLogger(EventLogLevel.Warning), enablePiiLogging: false) .WithClientCapabilities(_capabilities); - Log.Debug($"Created PublicClientApplicationBuilder"); + _log.Debug($"Created PublicClientApplicationBuilder"); } public async void InitializePublicClientAppForWAMBrokerAsync() @@ -75,11 +75,11 @@ public async void InitializePublicClientAppForWAMBrokerAsync() }); PublicClientApplication = PublicClientApplicationBuilder.Build(); - Log.Debug($"PublicClientApplication is instantiated"); + _log.Debug($"PublicClientApplication is instantiated"); } else { - Log.Error($"PublicClientApplicationBuilder is not initialized"); + _log.Error($"PublicClientApplicationBuilder is not initialized"); throw new ArgumentNullException(null); } @@ -101,11 +101,11 @@ public async Task InitializePublicClientAppForWAMBrokerAsyncWithParentWindow(Win }); PublicClientApplication = PublicClientApplicationBuilder.Build(); - Log.Debug($"PublicClientApplication is instantiated for interactive UI capability"); + _log.Debug($"PublicClientApplication is instantiated for interactive UI capability"); } else { - Log.Error($"Incorrect parameters to instantiate PublicClientApplication with interactive UI capability"); + _log.Error($"Incorrect parameters to instantiate PublicClientApplication with interactive UI capability"); throw new ArgumentNullException(null); } @@ -119,14 +119,14 @@ private async Task> TokenCacheRegistration() if (PublicClientApplication != null) { msalCacheHelper.RegisterCache(PublicClientApplication.UserTokenCache); - Log.Debug($"Token cache is successfully registered with PublicClientApplication"); + _log.Debug($"Token cache is successfully registered with PublicClientApplication"); // In the case the cache file is being reused there will be preexisting logged in accounts return await PublicClientApplication.GetAccountsAsync().ConfigureAwait(false); } else { - Log.Error($"Error encountered while registering token cache"); + _log.Error($"Error encountered while registering token cache"); } return Enumerable.Empty(); @@ -177,7 +177,7 @@ public async Task> GetAllStoredLoginIdsAsync() // in the application. // If a user has explicitly signed out of the connected Windows account, WAM will // remember and not acquire a token on behalf of a user. - Log.Debug($"Enable SSO for Azure Extension by connecting the Windows's default account"); + _log.Debug($"Enable SSO for Azure Extension by connecting the Windows's default account"); AuthenticationResult = null; try { @@ -193,7 +193,7 @@ public async Task> GetAllStoredLoginIdsAsync() } AuthenticationResult = await silentTokenAcquisitionBuilder.ExecuteAsync(); - Log.Information($"Azure SSO enabled"); + _log.Information($"Azure SSO enabled"); return AuthenticationResult.Account; } } @@ -201,7 +201,7 @@ public async Task> GetAllStoredLoginIdsAsync() catch (Exception ex) { // This is best effort - Log.Information($"Azure SSO failed with exception:{ex}"); + _log.Information($"Azure SSO failed with exception:{ex}"); } return null; @@ -209,7 +209,7 @@ public async Task> GetAllStoredLoginIdsAsync() public async Task> AcquireAllDeveloperAccountTokens(string[] scopes) { - Log.Debug($"AcquireAllDeveloperAccountTokens"); + _log.Debug($"AcquireAllDeveloperAccountTokens"); var remediationAccountIdentifiers = new List(); if (PublicClientApplication != null) { @@ -229,7 +229,7 @@ public async Task> AcquireAllDeveloperAccountTokens(string[] } catch (Exception ex) { - Log.Information($"AcquireAllDeveloperAccountTokens failed {ex}"); + _log.Information($"AcquireAllDeveloperAccountTokens failed {ex}"); remediationAccountIdentifiers.Add(account.Username); } } @@ -251,23 +251,23 @@ public async Task> AcquireAllDeveloperAccountTokens(string[] { if (msalClientEx.ErrorCode == MsalError.AuthenticationCanceledError) { - Log.Information($"MSALClient: User canceled authentication:{msalClientEx}"); + _log.Information($"MSALClient: User canceled authentication:{msalClientEx}"); } else { - Log.Error($"MSALClient: Error Acquiring Token:{msalClientEx}"); + _log.Error($"MSALClient: Error Acquiring Token:{msalClientEx}"); } } catch (MsalException msalEx) { - Log.Error($"MSAL: Error Acquiring Token:{msalEx}"); + _log.Error($"MSAL: Error Acquiring Token:{msalEx}"); } catch (Exception authenticationException) { - Log.Error($"Authentication: Error Acquiring Token:{authenticationException}"); + _log.Error($"Authentication: Error Acquiring Token:{authenticationException}"); } - Log.Information($"MSAL: Signed in user by acquiring token interactively."); + _log.Information($"MSAL: Signed in user by acquiring token interactively."); } return AuthenticationResult; @@ -286,7 +286,7 @@ public async Task> AcquireAllDeveloperAccountTokens(string[] public async Task ObtainTokenForLoggedInDeveloperAccount(string[] scopes, string loginId) { - Log.Information($"ObtainTokenForLoggedInDeveloperAccount"); + _log.Information($"ObtainTokenForLoggedInDeveloperAccount"); AuthenticationResult = null; var existingAccount = await GetDeveloperAccountFromCache(loginId); @@ -305,22 +305,22 @@ public async Task> AcquireAllDeveloperAccountTokens(string[] } catch (MsalUiRequiredException ex) { - Log.Error($"AcquireDeveloperAccountToken failed and requires user interaction {ex}"); + _log.Error($"AcquireDeveloperAccountToken failed and requires user interaction {ex}"); throw; } catch (MsalServiceException ex) { - Log.Error($"AcquireDeveloperAccountToken failed with MSAL service error: {ex}"); + _log.Error($"AcquireDeveloperAccountToken failed with MSAL service error: {ex}"); throw; } catch (MsalClientException ex) { - Log.Error($"AcquireDeveloperAccountToken failed with MSAL client error: {ex}"); + _log.Error($"AcquireDeveloperAccountToken failed with MSAL client error: {ex}"); throw; } catch (Exception ex) { - Log.Error($"AcquireDeveloperAccountToken failed with error: {ex}"); + _log.Error($"AcquireDeveloperAccountToken failed with error: {ex}"); throw; } } @@ -345,17 +345,17 @@ public async Task SignOutDeveloperIdAsync(string username) if (account.Username == username) { await PublicClientApplication.RemoveAsync(account).ConfigureAwait(false); - Log.Information($"MSAL: Signed out user."); + _log.Information($"MSAL: Signed out user."); } else { - Log.Warning($"MSAL: User is already absent from cache ."); + _log.Warning($"MSAL: User is already absent from cache ."); } } } else { - Log.Error($"Cannot login developer id as PublicClientApplication is null"); + _log.Error($"Cannot login developer id as PublicClientApplication is null"); throw new ArgumentNullException(null); } } diff --git a/src/AzureExtension/Helpers/AzureRepositoryHierarchy.cs b/src/AzureExtension/Helpers/AzureRepositoryHierarchy.cs index abc8f68d..18adbfb7 100644 --- a/src/AzureExtension/Helpers/AzureRepositoryHierarchy.cs +++ b/src/AzureExtension/Helpers/AzureRepositoryHierarchy.cs @@ -17,9 +17,9 @@ namespace DevHomeAzureExtension.Helpers; /// public class AzureRepositoryHierarchy { - private static readonly Lazy _log = new(() => Serilog.Log.ForContext("SourceContext", nameof(AzureRepositoryHierarchy))); + private static readonly Lazy _logger = new(() => Serilog.Log.ForContext("SourceContext", nameof(AzureRepositoryHierarchy))); - private static readonly ILogger Log = _log.Value; + private static readonly ILogger _log = _logger.Value; private readonly object _getOrganizationsLock = new(); @@ -156,7 +156,7 @@ private List QueryForProjects(Organization organization) } catch (Exception e) { - Log.Error(e, e.Message); + _log.Error(e, e.Message); } return new List(); diff --git a/src/AzureExtension/Notifications/NotificationHandler.cs b/src/AzureExtension/Notifications/NotificationHandler.cs index 63ee2cad..6e4e9b87 100644 --- a/src/AzureExtension/Notifications/NotificationHandler.cs +++ b/src/AzureExtension/Notifications/NotificationHandler.cs @@ -11,9 +11,9 @@ namespace DevHomeAzureExtension.Notifications; public class NotificationHandler { - private static readonly Lazy _log = new(() => Serilog.Log.ForContext("SourceContext", nameof(NotificationHandler))); + private static readonly Lazy _logger = new(() => Serilog.Log.ForContext("SourceContext", nameof(NotificationHandler))); - private static readonly ILogger Log = _log.Value; + private static readonly ILogger _log = _logger.Value; #pragma warning disable IDE0060 // Remove unused parameter public static void OnNotificationInvoked(object sender, AppNotificationActivatedEventArgs args) => NotificationActivation(args); @@ -21,7 +21,7 @@ public class NotificationHandler public static void NotificationActivation(AppNotificationActivatedEventArgs args) { - Log.Information($"Notification Activated with args: {NotificationArgsToString(args)}"); + _log.Information($"Notification Activated with args: {NotificationArgsToString(args)}"); if (args.Arguments.TryGetValue("htmlurl", out var urlString)) { @@ -30,7 +30,7 @@ public static void NotificationActivation(AppNotificationActivatedEventArgs args // Do not assume this string is a safe URL and blindly execute it; verify that it is // in fact a valid Azure URL. // TODO: Validate Azure URL - Log.Information($"Launching Uri: {urlString}"); + _log.Information($"Launching Uri: {urlString}"); var processStartInfo = new ProcessStartInfo { UseShellExecute = true, @@ -41,7 +41,7 @@ public static void NotificationActivation(AppNotificationActivatedEventArgs args } catch (Exception ex) { - Log.Error(ex, $"Failed launching Uri for {args.Arguments["htmlurl"]}"); + _log.Error(ex, $"Failed launching Uri for {args.Arguments["htmlurl"]}"); } return; diff --git a/src/AzureExtension/Providers/SettingsUIController.cs b/src/AzureExtension/Providers/SettingsUIController.cs index 0b9f21b7..a3224fb5 100644 --- a/src/AzureExtension/Providers/SettingsUIController.cs +++ b/src/AzureExtension/Providers/SettingsUIController.cs @@ -14,9 +14,9 @@ namespace DevHomeAzureExtension.Providers; internal sealed partial class SettingsUIController : IExtensionAdaptiveCardSession { - private static readonly Lazy _log = new(() => Serilog.Log.ForContext("SourceContext", nameof(SettingsUIController))); + private static readonly Lazy _logger = new(() => Serilog.Log.ForContext("SourceContext", nameof(SettingsUIController))); - private static readonly ILogger Log = _log.Value; + private static readonly ILogger _log = _logger.Value; private readonly IAICredentialService _aiCredentialService; @@ -55,7 +55,7 @@ private ProviderOperationResult UpdateCard() } catch (Exception ex) { - Log.Error(ex, "Failed to update settings card"); + _log.Error(ex, "Failed to update settings card"); return new ProviderOperationResult(ProviderOperationStatus.Failure, ex, ex.Message, ex.Message); } } @@ -97,7 +97,7 @@ public IAsyncOperation OnAction(string action, string i { if (adaptiveCardPayload.IsClearOpenAIKeyAction) { - Log.Information("Clearing OpenAI key"); + _log.Information("Clearing OpenAI key"); _aiCredentialService.RemoveCredentials(OpenAIDevContainerQuickStartProjectProvider.LoginId, OpenAIDevContainerQuickStartProjectProvider.LoginId); return UpdateCard(); } @@ -105,7 +105,7 @@ public IAsyncOperation OnAction(string action, string i } catch (Exception ex) { - Log.Error(ex, "Failed to clear OpenAI key"); + _log.Error(ex, "Failed to clear OpenAI key"); return new ProviderOperationResult(ProviderOperationStatus.Failure, ex, ex.Message, ex.Message); } From 0b0a391d6423ff6f21e6ab3517e93d15f06b3eea Mon Sep 17 00:00:00 2001 From: Huzaifa Danish Date: Thu, 27 Jun 2024 15:24:53 -0700 Subject: [PATCH 05/11] Added filtering, sorting and location to Dev Box creation flow (#226) * Added locations * Initial test changes * Moved to Management Service * Housekeeping * Fixed sorting * Changed function type and name * Merge conflict fix * Added display name --------- Co-authored-by: Huzaifa Danish --- .../DevBoxProjectProperties.cs | 4 +- .../Helpers/AbilitiesJSONToCSClasses.cs | 28 +++++++++++ .../Models/CreationAdaptiveCardSession.cs | 4 +- .../DevBox/DevBoxManagementService.cs | 50 ++++++++++++++++++- 4 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 src/AzureExtension/DevBox/Helpers/AbilitiesJSONToCSClasses.cs diff --git a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxProjectProperties.cs b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxProjectProperties.cs index d29bbc9f..27467c55 100644 --- a/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxProjectProperties.cs +++ b/src/AzureExtension/DevBox/DevBoxJsonToCsClasses/DevBoxProjectProperties.cs @@ -15,5 +15,7 @@ public class DevBoxProjectProperties public string DevCenterId { get; set; } = string.Empty; - public string Description { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + + public string DisplayName { get; set; } = string.Empty; } diff --git a/src/AzureExtension/DevBox/Helpers/AbilitiesJSONToCSClasses.cs b/src/AzureExtension/DevBox/Helpers/AbilitiesJSONToCSClasses.cs new file mode 100644 index 00000000..eb822064 --- /dev/null +++ b/src/AzureExtension/DevBox/Helpers/AbilitiesJSONToCSClasses.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace DevHomeAzureExtension.DevBox.Helpers; + +// Example of a task JSON response that will be deserialized into the BaseClass class +// { +// "abilitiesAsAdmin": [], +// "abilitiesAsDeveloper": [ +// "ReadDevBoxes", +// "WriteDevBoxes", +// ... +// ] +// } +// + +/// +/// Represents the class for the abilities JSON response. +/// +public class AbilitiesJSONToCSClasses +{ + public class BaseClass + { + public List AbilitiesAsAdmin { get; set; } = new(); + + public List AbilitiesAsDeveloper { get; set; } = new(); + } +} diff --git a/src/AzureExtension/DevBox/Models/CreationAdaptiveCardSession.cs b/src/AzureExtension/DevBox/Models/CreationAdaptiveCardSession.cs index 0de4cb11..fbcd331e 100644 --- a/src/AzureExtension/DevBox/Models/CreationAdaptiveCardSession.cs +++ b/src/AzureExtension/DevBox/Models/CreationAdaptiveCardSession.cs @@ -341,7 +341,7 @@ private void BuildAllProjectsAndPoolJsonObjects() // Add information for the specific project to the project array var projectInfo = new JsonObject { - { "title", container.Project!.Name }, + { "title", container.Project!.Properties.DisplayName.Length > 0 ? container.Project!.Properties.DisplayName : container.Project!.Name }, { "value", $"{i}" }, }; @@ -357,7 +357,7 @@ private void BuildAllProjectsAndPoolJsonObjects() var poolHardwareSpecs = Resources.GetResource("DevBox_PoolSubtitle", pool.HardwareProfile.VCPUs, pool.HardwareProfile.MemoryGB, pool.StorageProfile.OsDisk.DiskSizeGB); var poolInfo = new JsonObject { - { "title", $"{pool.Name}" }, + { "title", $"{pool.Name} ({pool.Location})" }, { "subtitle", $"{poolHardwareSpecs}" }, { "value", $"{k}" }, }; diff --git a/src/AzureExtension/Services/DevBox/DevBoxManagementService.cs b/src/AzureExtension/Services/DevBox/DevBoxManagementService.cs index da4315ff..285e1368 100644 --- a/src/AzureExtension/Services/DevBox/DevBoxManagementService.cs +++ b/src/AzureExtension/Services/DevBox/DevBoxManagementService.cs @@ -9,6 +9,7 @@ using DevHomeAzureExtension.Contracts; using DevHomeAzureExtension.DevBox.DevBoxJsonToCsClasses; using DevHomeAzureExtension.DevBox.Exceptions; +using DevHomeAzureExtension.DevBox.Helpers; using DevHomeAzureExtension.DevBox.Models; using Microsoft.Windows.DevHome.SDK; using Serilog; @@ -32,6 +33,14 @@ public class DevBoxManagementService : IDevBoxManagementService private const string DevBoxManagementServiceName = nameof(DevBoxManagementService); + private readonly JsonSerializerOptions _jsonOptions = new() + { + PropertyNameCaseInsensitive = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + }; + + private const string _writeAbility = "WriteDevBoxes"; + /// /> public async Task HttpsRequestToManagementPlane(Uri webUri, IDeveloperId developerId, HttpMethod method, HttpContent? requestContent = null) { @@ -119,6 +128,31 @@ public async Task HttpsRequestToDataPlaneWithRawResponse(HttpClient clie } } + // Filter out projects that the user does not have write access to + private bool DeveloperHasWriteAbility(DevBoxProject project, IDeveloperId developerId) + { + try + { + var uri = $"{project.Properties.DevCenterUri}{DevBoxConstants.Projects}/{project.Name}/users/me/abilities?{DevBoxConstants.APIVersion}"; + var result = HttpsRequestToDataPlane(new Uri(uri), developerId, HttpMethod.Get, null).Result; + var rawResponse = result.JsonResponseRoot.ToString(); + var abilities = JsonSerializer.Deserialize(rawResponse, _jsonOptions); + _log.Debug($"Response from abilities: {rawResponse}"); + + if (abilities!.AbilitiesAsDeveloper.Contains(_writeAbility) || abilities!.AbilitiesAsAdmin.Contains(_writeAbility)) + { + return true; + } + + return false; + } + catch (Exception ex) + { + _log.Error(ex, $"Unable to get abilities for {project.Name}"); + return true; + } + } + /// public async Task> GetAllProjectsToPoolsMappingAsync(DevBoxProjects projects, IDeveloperId developerId) { @@ -137,14 +171,25 @@ public async Task> GetAllProjectsToPoolsMapp await Parallel.ForEachAsync(projects.Data!, async (project, token) => { + if (!DeveloperHasWriteAbility(project, developerId)) + { + return; + } + try { var properties = project.Properties; var uriToRetrievePools = $"{properties.DevCenterUri}{DevBoxConstants.Projects}/{project.Name}/{DevBoxConstants.Pools}?{DevBoxConstants.APIVersion}"; var result = await HttpsRequestToDataPlane(new Uri(uriToRetrievePools), developerId, HttpMethod.Get); var pools = JsonSerializer.Deserialize(result.JsonResponseRoot.ToString(), DevBoxConstants.JsonOptions); - var container = new DevBoxProjectAndPoolContainer { Project = project, Pools = pools }; + // Sort the pools by name, case insensitive + if (pools?.Value != null) + { + pools.Value = new(pools.Value.OrderBy(x => x.Name)); + } + + var container = new DevBoxProjectAndPoolContainer { Project = project, Pools = pools }; projectsToPoolsMapping.Add(container); } catch (Exception ex) @@ -153,6 +198,9 @@ await Parallel.ForEachAsync(projects.Data!, async (project, token) => } }); + // Sort the mapping by project name, case insensitive + projectsToPoolsMapping = new(projectsToPoolsMapping.OrderByDescending(x => x.Project?.Name)); + _projectAndPoolContainerMap.Add(uniqueUserId, projectsToPoolsMapping.ToList()); return projectsToPoolsMapping.ToList(); } From 57e9bf4979eb447c74aabdbb693b0a6c70295851 Mon Sep 17 00:00:00 2001 From: Felipe G Date: Mon, 1 Jul 2024 14:13:59 -0700 Subject: [PATCH 06/11] Adding PR widgets title customization option (#225) * Adding PR widgets title customization option * spacing * change in comment * Changing variable name * Reverting space changes on .resw file * Reverting spaces part 2 --------- Co-authored-by: Felipe da Conceicao Guimaraes --- src/AzureExtension/Helpers/Resources.cs | 4 +- .../Strings/en-US/Resources.resw | 8 +- ...zurePullRequestsConfigurationTemplate.json | 7 + .../AzureQueryListConfigurationTemplate.json | 194 +++++++++--------- 4 files changed, 110 insertions(+), 103 deletions(-) diff --git a/src/AzureExtension/Helpers/Resources.cs b/src/AzureExtension/Helpers/Resources.cs index c829e431..89d6254e 100644 --- a/src/AzureExtension/Helpers/Resources.cs +++ b/src/AzureExtension/Helpers/Resources.cs @@ -108,8 +108,8 @@ public static string[] GetWidgetResourceIdentifiers() "Widget_Template/PRDefaultView", "Widget_Template_ErrorMessage/QueryURL", "Widget_Template/QueryURLLabel", - "Widget_Template/QueryTitlePlaceholder", - "Widget_Template/QueryTitleLabel", + "Widget_Template/WidgetTitlePlaceholder", + "Widget_Template/WidgetTitleLabel", "Widget_Template/NumberOfTiles", "Widget_Template_Button/AddTile", "Widget_Template_Button/RemoveTile", diff --git a/src/AzureExtension/Strings/en-US/Resources.resw b/src/AzureExtension/Strings/en-US/Resources.resw index c6f254d7..504001cd 100644 --- a/src/AzureExtension/Strings/en-US/Resources.resw +++ b/src/AzureExtension/Strings/en-US/Resources.resw @@ -254,13 +254,13 @@ Created by me Shown in Pull Request Widget, filter for items - + Title: Shown in Widget - - Query title - Shown in Widget, placeholder for query title + + Widget title + Shown in Widget, placeholder for widget title Query URL: diff --git a/src/AzureExtension/Widgets/Templates/AzurePullRequestsConfigurationTemplate.json b/src/AzureExtension/Widgets/Templates/AzurePullRequestsConfigurationTemplate.json index 82b9c8a1..2d5dc5ee 100644 --- a/src/AzureExtension/Widgets/Templates/AzurePullRequestsConfigurationTemplate.json +++ b/src/AzureExtension/Widgets/Templates/AzurePullRequestsConfigurationTemplate.json @@ -29,6 +29,13 @@ "isRequired": true, "errorMessage": "%Widget_Template_ErrorMessage/RepositoryURL%" }, + { + "type": "Input.Text", + "placeholder": "%Widget_Template/WidgetTitlePlaceholder%", + "id": "widgetTitle", + "label": "%Widget_Template/WidgetTitleLabel%", + "value": "${widgetTitle}" + }, { "type": "Input.ChoiceSet", "choices": [ diff --git a/src/AzureExtension/Widgets/Templates/AzureQueryListConfigurationTemplate.json b/src/AzureExtension/Widgets/Templates/AzureQueryListConfigurationTemplate.json index d6b4720f..87cbdb26 100644 --- a/src/AzureExtension/Widgets/Templates/AzureQueryListConfigurationTemplate.json +++ b/src/AzureExtension/Widgets/Templates/AzureQueryListConfigurationTemplate.json @@ -1,98 +1,98 @@ -{ - "type": "AdaptiveCard", - "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", - "version": "1.5", - "body": [ - { - "type": "Container", - "items": [ - { - "type": "Input.ChoiceSet", - "id": "account", - "placeholder": "%Widget_Template/ChooseAccountPlaceholder%", - "value": "${selectedDevId}", - "choices": [ - { - "$data": "${accounts}", - "title": "${devid}", - "value": "${devid}" - } - ] - }, - { - "type": "Input.Text", - "placeholder": "%Widget_Template/EnterURLPlaceholder%", - "id": "query", - "value": "${url}", - "label": "%Widget_Template/QueryURLLabel%", - "style": "Url", - "isRequired": true, - "errorMessage": "%Widget_Template_ErrorMessage/QueryURL%" - }, - { - "type": "Input.Text", - "placeholder": "%Widget_Template/QueryTitlePlaceholder%", - "id": "widgetTitle", - "label": "%Widget_Template/QueryTitleLabel%", - "value": "${widgetTitle}" - }, - { - "type": "Container", - "$when": "${message != null}", - "items": [ - { - "type": "TextBlock", - "text": "${message}", - "wrap": true, - "size": "small" - } - ], - "style": "warning" - }, - { - "type": "ColumnSet", - "spacing": "Medium", - "columns": [ - { - "type": "Column", - "width": "stretch" - }, - { - "type": "Column", - "width": "auto", - "items": [ - { - "type": "Container", - "items": [ - { - "type": "ActionSet", - "actions": [ - { - "type": "Action.Execute", - "title": "%Widget_Template_Button/Save%", - "verb": "Save", - "role": "Button", - "associatedInputs": "auto" - }, - { - "type": "Action.Execute", - "title": "%Widget_Template_Button/Cancel%", - "verb": "Cancel", - "isEnabled": "${$root.pinned}" - } - ] - } - ] - } - ] - }, - { - "type": "Column", - "width": "stretch" - } - ] - } - ] - } - ] +{ + "type": "AdaptiveCard", + "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", + "version": "1.5", + "body": [ + { + "type": "Container", + "items": [ + { + "type": "Input.ChoiceSet", + "id": "account", + "placeholder": "%Widget_Template/ChooseAccountPlaceholder%", + "value": "${selectedDevId}", + "choices": [ + { + "$data": "${accounts}", + "title": "${devid}", + "value": "${devid}" + } + ] + }, + { + "type": "Input.Text", + "placeholder": "%Widget_Template/EnterURLPlaceholder%", + "id": "query", + "value": "${url}", + "label": "%Widget_Template/QueryURLLabel%", + "style": "Url", + "isRequired": true, + "errorMessage": "%Widget_Template_ErrorMessage/QueryURL%" + }, + { + "type": "Input.Text", + "placeholder": "%Widget_Template/WidgetTitlePlaceholder%", + "id": "widgetTitle", + "label": "%Widget_Template/WidgetTitleLabel%", + "value": "${widgetTitle}" + }, + { + "type": "Container", + "$when": "${message != null}", + "items": [ + { + "type": "TextBlock", + "text": "${message}", + "wrap": true, + "size": "small" + } + ], + "style": "warning" + }, + { + "type": "ColumnSet", + "spacing": "Medium", + "columns": [ + { + "type": "Column", + "width": "stretch" + }, + { + "type": "Column", + "width": "auto", + "items": [ + { + "type": "Container", + "items": [ + { + "type": "ActionSet", + "actions": [ + { + "type": "Action.Execute", + "title": "%Widget_Template_Button/Save%", + "verb": "Save", + "role": "Button", + "associatedInputs": "auto" + }, + { + "type": "Action.Execute", + "title": "%Widget_Template_Button/Cancel%", + "verb": "Cancel", + "isEnabled": "${$root.pinned}" + } + ] + } + ] + } + ] + }, + { + "type": "Column", + "width": "stretch" + } + ] + } + ] + } + ] } \ No newline at end of file From 33f3cc62fb8c8962d1fd5552b8e0616af8ab8286 Mon Sep 17 00:00:00 2001 From: David Bennett Date: Tue, 2 Jul 2024 09:33:47 -0700 Subject: [PATCH 07/11] Add Repository, Project, and Organization Caching Infrastructure (#229) --- src/AzureExtension/AzureExtension.csproj | 6 + .../DataManager/AzureDataManager.cs | 31 +- .../DataManager/AzureDataManagerCache.cs | 320 ++++++++++++++ .../DataManager/CacheManager.cs | 344 ++++++++++++++ .../CacheManagerUpdateEventArgs.cs | 31 ++ .../DataManager/DataManagerUpdateEventArgs.cs | 2 + .../DataManager/IAzureDataManager.cs | 10 + .../DataManager/RequestOptions.cs | 4 + .../DataModel/AzureDataStoreSchema.cs | 51 ++- .../DataModel/DataObjects/Identity.cs | 71 ++- .../DataModel/DataObjects/Organization.cs | 303 +++++++------ .../DataModel/DataObjects/Project.cs | 418 ++++++++++-------- .../DataModel/DataObjects/ProjectReference.cs | 103 +++++ .../DataModel/DataObjects/PullRequests.cs | 326 +++++++------- .../DataModel/DataObjects/Query.cs | 300 ++++++------- .../DataModel/DataObjects/Repository.cs | 190 ++++++++ .../DataObjects/RepositoryReference.cs | 111 +++++ .../DataModel/DataObjects/WorkItemType.cs | 2 +- src/AzureExtension/Helpers/FileHelper.cs | 60 +++ src/AzureExtension/Helpers/Json.cs | 73 +++ src/AzureExtension/Helpers/LocalSettings.cs | 76 ++++ src/AzureExtension/Helpers/Resources.cs | 10 +- src/AzureExtension/Helpers/RuntimeHelper.cs | 19 + src/AzureExtension/NativeMethods.txt | 13 +- .../Providers/DevHomeRepository.cs | 9 + .../Providers/SettingsCardData.cs | 15 + .../SettingsCardSerializerContext.cs | 12 + .../Providers/SettingsCardTemplate.json | 62 +++ .../Providers/SettingsProvider.cs | 61 +-- .../Providers/SettingsUIController.cs | 311 +++++++------ .../Strings/en-US/Resources.resw | 73 +-- .../Widgets/AzurePullRequestsWidget.cs | 4 +- .../Widgets/AzureQueryListWidget.cs | 4 +- src/AzureExtension/Widgets/AzureWidget.cs | 4 +- src/AzureExtensionServer/Program.cs | 6 +- .../AzureExtension/AzureExtension.Test.csproj | 2 +- .../DataStore/DataObjectTests.cs | 35 +- 37 files changed, 2604 insertions(+), 868 deletions(-) create mode 100644 src/AzureExtension/DataManager/AzureDataManagerCache.cs create mode 100644 src/AzureExtension/DataManager/CacheManager.cs create mode 100644 src/AzureExtension/DataManager/CacheManagerUpdateEventArgs.cs create mode 100644 src/AzureExtension/DataModel/DataObjects/ProjectReference.cs create mode 100644 src/AzureExtension/DataModel/DataObjects/Repository.cs create mode 100644 src/AzureExtension/DataModel/DataObjects/RepositoryReference.cs create mode 100644 src/AzureExtension/Helpers/FileHelper.cs create mode 100644 src/AzureExtension/Helpers/Json.cs create mode 100644 src/AzureExtension/Helpers/LocalSettings.cs create mode 100644 src/AzureExtension/Helpers/RuntimeHelper.cs create mode 100644 src/AzureExtension/Providers/SettingsCardData.cs create mode 100644 src/AzureExtension/Providers/SettingsCardSerializerContext.cs create mode 100644 src/AzureExtension/Providers/SettingsCardTemplate.json diff --git a/src/AzureExtension/AzureExtension.csproj b/src/AzureExtension/AzureExtension.csproj index 6c9aa157..b1eb6a14 100644 --- a/src/AzureExtension/AzureExtension.csproj +++ b/src/AzureExtension/AzureExtension.csproj @@ -211,6 +211,12 @@ + + + Always + + + $(DefineConstants);CANARY_BUILD $(DefineConstants);STABLE_BUILD diff --git a/src/AzureExtension/DataManager/AzureDataManager.cs b/src/AzureExtension/DataManager/AzureDataManager.cs index e4706ceb..c0466c48 100644 --- a/src/AzureExtension/DataManager/AzureDataManager.cs +++ b/src/AzureExtension/DataManager/AzureDataManager.cs @@ -696,6 +696,11 @@ private static void SendErrorUpdateEvent(ILogger logger, object? source, Guid re SendUpdateEvent(logger, source, DataManagerUpdateKind.Error, requestor, context, ex); } + private static void SendCancelUpdateEvent(ILogger logger, object? source, Guid requestor, dynamic context, Exception? ex = null) + { + SendUpdateEvent(logger, source, DataManagerUpdateKind.Cancel, requestor, context, ex); + } + private static void SendUpdateEvent(ILogger logger, object? source, DataManagerUpdateKind kind, Guid requestor, dynamic context, Exception? ex = null) { if (OnUpdate != null) @@ -757,6 +762,30 @@ public static ConnectionResult GetConnection(Uri connectionUri, DeveloperId.Deve return result; } + public IEnumerable GetRepositories() + { + ValidateDataStore(); + return Repository.GetAllWithReference(DataStore); + } + + public string GetMetaData(string key) + { + var metaData = MetaData.GetByKey(DataStore, key); + if (metaData is null) + { + return string.Empty; + } + else + { + return metaData.Value; + } + } + + public void SetMetaData(string key, string value) + { + MetaData.AddOrUpdate(DataStore, key, value); + } + // Removes unused data from the datastore. private void PruneObsoleteData() { @@ -774,7 +803,7 @@ private void PruneObsoleteData() // Sets a last-updated in the MetaData. private void SetLastUpdatedInMetaData() { - MetaData.AddOrUpdate(DataStore, _lastUpdatedKeyName, DateTime.Now.ToDataStoreString()); + MetaData.AddOrUpdate(DataStore, _lastUpdatedKeyName, DateTime.UtcNow.ToDataStoreString()); } private void ValidateDataStore() diff --git a/src/AzureExtension/DataManager/AzureDataManagerCache.cs b/src/AzureExtension/DataManager/AzureDataManagerCache.cs new file mode 100644 index 00000000..50a8590d --- /dev/null +++ b/src/AzureExtension/DataManager/AzureDataManagerCache.cs @@ -0,0 +1,320 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Dynamic; +using DevHomeAzureExtension.Client; +using DevHomeAzureExtension.DataManager; +using DevHomeAzureExtension.DataModel; +using DevHomeAzureExtension.DeveloperId; +using Microsoft.TeamFoundation.Core.WebApi; +using Microsoft.TeamFoundation.SourceControl.WebApi; +using Microsoft.VisualStudio.Services.Account; +using Microsoft.VisualStudio.Services.Account.Client; +using Microsoft.VisualStudio.Services.Common; +using Microsoft.VisualStudio.Services.WebApi; +using Serilog; + +namespace DevHomeAzureExtension; + +public partial class AzureDataManager +{ + private static readonly Uri _vSAccountUri = new(@"https://app.vssps.visualstudio.com/"); + + private static readonly int _pullRequestProjectLimit = 50; + + private static readonly int _pullRequestRepositoryLimit = 25; + + public async Task UpdateDataForAccountsAsync(RequestOptions? options = null, Guid? requestor = null) + { + // A parameterless call will always update the data, effectively a 'force update'. + await UpdateDataForAccountsAsync(TimeSpan.MinValue, options, requestor); + } + +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + public async Task UpdateDataForAccountsAsync(TimeSpan olderThan, RequestOptions? options = null, Guid? requestor = null) +#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously + { + var cancellationToken = options?.CancellationToken.GetValueOrDefault() ?? default; + var requestorGuid = requestor ?? Guid.Empty; + var errors = 0; + var accountsUpdated = 0; + var accountsSkipped = 0; + var startTime = DateTime.UtcNow; + Exception? firstException = null; + + // Refresh option will clear all sync data, effectively forcing every row to be updated. + if (options is not null && options.Refresh) + { + using var tx = DataStore.Connection!.BeginTransaction(); + try + { + _log.Debug("Clearing sync data."); + Organization.ClearAllSyncData(DataStore); + } + catch (Exception ex) + { + _log.Error(ex, "Failed clearing sync data."); + tx.Rollback(); + } + + tx.Commit(); + } + + IEnumerable? developerIds; + try + { + developerIds = DeveloperIdProvider.GetInstance().GetLoggedInDeveloperIdsInternal(); + } + catch (Exception ex) + { + _log.Error(ex, "Failed getting logged in developer ids."); + firstException = ex; + ++errors; + developerIds = []; + } + + foreach (var developerId in developerIds) + { + _log.Debug($"Updating accounts for {developerId.LoginId}, older than {olderThan}"); + var accounts = GetAccounts(developerId, cancellationToken); + foreach (var account in accounts) + { + var org = Organization.Get(DataStore, account.AccountUri.ToString()); + if (org is not null && ((DateTime.UtcNow - org.LastSyncAt) < olderThan)) + { + _log.Debug($"Organization: {org.Name} has recently been updated, skipping."); + ++accountsSkipped; + continue; + } + + // Transactional unit is the account (organization), which chains down to Project + // and repository. + using var tx = DataStore.Connection!.BeginTransaction(); + try + { + cancellationToken.ThrowIfCancellationRequested(); + var connection = AzureClientProvider.GetConnectionForLoggedInDeveloper(account.AccountUri, developerId); + + // If connection is null the organization is disabled + if (connection is null) + { + ++accountsSkipped; + _log.Information($"Account {account.AccountName} had a null connection, treating as disabled."); + tx.Rollback(); + continue; + } + + UpdateOrganization(account, developerId, connection, cancellationToken); + + tx.Commit(); + _log.Information($"Updated organization: {account.AccountName}"); + ++accountsUpdated; + } + catch (Exception ex) when (IsCancelException(ex)) + { + firstException ??= ex; + tx.Rollback(); + _log.Information("Operation was cancelled."); + var cancelContext = CreateUpdateEventContext(errors, accountsUpdated, accountsSkipped, DateTime.UtcNow - startTime); + SendCancelUpdateEvent(_log, this, requestorGuid, cancelContext, firstException); + return; + } + catch (Exception ex) + { + firstException ??= ex; + tx.Rollback(); + _log.Error(ex, $"Unexpected failure updating account: {account.AccountName}"); + ++errors; + } + } + } + + var elapsed = DateTime.UtcNow - startTime; + _log.Information($"Updating all account data complete. Elapsed: {elapsed.TotalSeconds}s"); + var context = CreateUpdateEventContext(errors, accountsUpdated, accountsSkipped, elapsed); + SendCacheUpdateEvent(_log, this, requestorGuid, context, firstException); + } + + // Returns a dynamic object with event context for reporting. + private dynamic CreateUpdateEventContext(int errors, int accountsUpdated, int accountsSkipped, TimeSpan elapsed) + { + dynamic context = new ExpandoObject(); + var contextDict = (IDictionary)context; + contextDict.Add("AccountsUpdated", accountsUpdated); + contextDict.Add("AccountsSkipped", accountsSkipped); + contextDict.Add("Errors", errors); + contextDict.Add("TimeElapsed", elapsed); + return context; + } + + private void UpdateOrganization(Account account, DeveloperId.DeveloperId developerId, VssConnection connection, CancellationToken cancellationToken) + { + // Update account identity information: + var identity = Identity.GetOrCreateIdentity(DataStore, connection.AuthorizedIdentity, connection, true); + + _log.Verbose($"Updating organization: {account.AccountName}"); + var organization = Organization.GetOrCreate(DataStore, account.AccountUri); + var projects = GetProjects(account, connection); + foreach (var project in projects) + { + cancellationToken.ThrowIfCancellationRequested(); + _log.Verbose($"Updating project: {project.Name}"); + var dsProject = Project.GetOrCreateByTeamProject(DataStore, project, organization.Id); + + // Get the project's pull request count for this developer, add it. + var projectPullRequestCount = GetPullRequestsForProject(project, connection, developerId, cancellationToken).Count; + if (projectPullRequestCount > 0) + { + ProjectReference.GetOrCreate(DataStore, dsProject.Id, identity.Id, projectPullRequestCount); + } + + var repositories = GetGitRepositories(project, connection, cancellationToken); + foreach (var repository in repositories) + { + cancellationToken.ThrowIfCancellationRequested(); + _log.Verbose($"Updating repository: {repository.Name}"); + var dsRepository = Repository.GetOrCreate(DataStore, repository, dsProject.Id); + + // If the project had pull requests, we need to check if this repository has any. + // We don't waste rest calls on every repository unless we know the project had at least one. + if (projectPullRequestCount > 0) + { + var repositoryPullRequestCount = GetPullRequestsForRepository(repository, connection, developerId, cancellationToken).Count; + if (repositoryPullRequestCount > 0) + { + // If non-zero, add a repository reference. + RepositoryReference.GetOrCreate(DataStore, dsRepository.Id, identity.Id, repositoryPullRequestCount); + } + } + } + } + + organization.SetSynced(); + } + + private List GetAccounts(DeveloperId.DeveloperId developerId, CancellationToken cancellationToken = default) + { + try + { + var connection = AzureClientProvider.GetConnectionForLoggedInDeveloper(_vSAccountUri, developerId); + var client = connection.GetClient(); + return client.GetAccountsByMemberAsync(memberId: connection.AuthorizedIdentity.Id, cancellationToken: cancellationToken).Result; + } + catch (Exception ex) when (!IsCancelException(ex)) + { + _log.Error(ex, "Failed querying for organizations."); + return []; + } + } + + private List GetProjects(Account account, VssConnection connection) + { + try + { + var client = connection.GetClient(); + var projects = client.GetProjects().Result; + return [.. projects]; + } + catch (VssServiceException vssEx) + { + _log.Information($"Failed getting projects for account: {account.AccountName}. {vssEx.Message}"); + } + catch (Exception ex) + { + _log.Error(ex, $"Failed getting projects for account: {account.AccountName}"); + } + + return []; + } + + private List GetGitRepositories(TeamProjectReference project, VssConnection connection, CancellationToken cancellationToken = default) + { + try + { + var gitClient = connection.GetClient(); + var repositories = gitClient.GetRepositoriesAsync(project.Id, false, false, cancellationToken).Result; + return [.. repositories]; + } + catch (Exception ex) when (!IsCancelException(ex)) + { + _log.Error(ex, $"Failed getting repositories for project: {project.Name}"); + } + + return []; + } + + private List GetPullRequestsForProject(TeamProjectReference project, VssConnection connection, DeveloperId.DeveloperId developerId, CancellationToken cancellationToken = default) + { + try + { + var gitClient = connection.GetClient(); + + // We are interested in pull requests where the developer is the author. + var searchCriteria = new GitPullRequestSearchCriteria + { + CreatorId = connection.AuthorizedIdentity.Id, + IncludeLinks = false, + Status = PullRequestStatus.All, + }; + + // We expect most results will be empty but for the sake of performance we will limit the number. + var pullRequests = gitClient.GetPullRequestsByProjectAsync(project.Id, searchCriteria, null, null, _pullRequestProjectLimit, null, cancellationToken).Result; + return [..pullRequests]; + } + catch (VssServiceException vssEx) + { + _log.Debug($"Unable to access project pull requests: {project.Name}: {vssEx.Message}"); + } + catch (Exception ex) when (!IsCancelException(ex)) + { + _log.Error(ex, $"Failed getting pull requests for project: {project.Name}"); + } + + return []; + } + + private List GetPullRequestsForRepository(GitRepository repository, VssConnection connection, DeveloperId.DeveloperId developerId, CancellationToken cancellationToken = default) + { + try + { + var gitClient = connection.GetClient(); + + // We are interested in pull requests where the developer is the author. + var searchCriteria = new GitPullRequestSearchCriteria + { + CreatorId = connection.AuthorizedIdentity.Id, + IncludeLinks = false, + Status = PullRequestStatus.All, + }; + + // We expect most results will be empty but for the sake of performance we will limit the number. + var pullRequests = gitClient.GetPullRequestsAsync(repository.Id, searchCriteria, null, null, _pullRequestRepositoryLimit, null, cancellationToken).Result; + return [.. pullRequests]; + } + catch (AggregateException aggEx) when (aggEx.InnerException is VssServiceException) + { + // There are likely many repositories to which the user does not have access. + // This is expected, and if the user does not have access then it is a safe + // assumption that they do not have any pull requests there, so we will not + // treat this as an error. + // Specific error is: TF401019 + _log.Debug($"Unable to access repository pull requests: {repository.Name}: {aggEx.InnerException?.Message}"); + } + catch (Exception ex) when (!IsCancelException(ex)) + { + _log.Error(ex, $"Failed getting pull requests for repository: {repository.Name}"); + } + + return []; + } + + private static void SendCacheUpdateEvent(ILogger logger, object? source, Guid requestor, dynamic context, Exception? ex) + { + SendUpdateEvent(logger, source, DataManagerUpdateKind.Cache, requestor, context, ex); + } + + private static bool IsCancelException(Exception ex) + { + return (ex is OperationCanceledException) || (ex is TaskCanceledException); + } +} diff --git a/src/AzureExtension/DataManager/CacheManager.cs b/src/AzureExtension/DataManager/CacheManager.cs new file mode 100644 index 00000000..feae06d8 --- /dev/null +++ b/src/AzureExtension/DataManager/CacheManager.cs @@ -0,0 +1,344 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using DevHomeAzureExtension.DeveloperId; +using DevHomeAzureExtension.Helpers; +using DevHomeAzureExtension.Providers; +using Microsoft.Windows.DevHome.SDK; +using Serilog; + +namespace DevHomeAzureExtension.DataManager; + +public class CacheManager : IDisposable +{ + private static readonly string _cacheManagerLastUpdatedMetaDataKey = "CacheManagerLastUpdated"; + + // Frequency the CacheManager checks for an update. + private static readonly TimeSpan _updateInterval = TimeSpan.FromHours(4); + + private static readonly TimeSpan _defaultAccountUpdateFrequency = TimeSpan.FromDays(3); + + private static readonly object _instanceLock = new(); + + private readonly object _stateLock = new(); + + private static CacheManager? _singletonInstance; + + private readonly ILogger _log; + + private CancellationTokenSource _cancelSource; + + private IAzureDataManager DataManager { get; } + + public bool UpdateInProgress { get; private set; } + + public DateTime LastUpdated + { + get => GetLastUpdated(); + private set => SetLastUpdated(value); + } + + // If the next update should clear sync data and force an update. + private bool _clearNextDataUpdate; + + // If a Refresh call is pending and has not yet completed. + private bool _pendingRefresh; + + public event CacheManagerUpdateEventHandler? OnUpdate; + + private DataUpdater DataUpdater { get; set; } + + private Guid Id { get; } + + private DateTime LastUpdateTime { get; set; } = DateTime.MinValue; + + public static CacheManager GetInstance() + { + try + { + lock (_instanceLock) + { + _singletonInstance ??= new CacheManager(); + } + + return _singletonInstance; + } + catch (Exception e) + { + var log = Log.ForContext("SourceContext", nameof(CacheManager)); + log.Error(e, $"Failed creating CacheManager."); + throw; + } + } + + private CacheManager() + { + Id = Guid.NewGuid(); + _log = Log.ForContext("SourceContext", $"{nameof(CacheManager)}"); + DataManager = AzureDataManager.CreateInstance("CacheManager") ?? throw new DataStoreInaccessibleException(); + DataUpdater = new DataUpdater(PeriodicUpdate); + AzureDataManager.OnUpdate += HandleDataManagerUpdate; + DeveloperIdProvider.GetInstance().Changed += HandleDeveloperIdChange; + _cancelSource = new CancellationTokenSource(); + } + + public void Start() + { + _log.Debug("Starting updater."); + _ = DataUpdater.Start(); + } + + public void Stop() + { + DataUpdater.Stop(); + } + + public void CancelUpdateInProgress() + { + lock (_stateLock) + { + if (UpdateInProgress && !_cancelSource.IsCancellationRequested) + { + _log.Information("Cancelling update."); + _cancelSource.Cancel(); + } + } + } + + public async Task Refresh() + { + CancelUpdateInProgress(); + + lock (_stateLock) + { + if (_pendingRefresh) + { + _log.Debug("Refresh already pending, ignoring refresh request."); + return; + } + + _pendingRefresh = true; + _clearNextDataUpdate = true; + } + + await Update(TimeSpan.MinValue); + } + + public IEnumerable GetRepositories() + { + var devHomeRepositories = new List(); + var repositories = DataManager.GetRepositories(); + foreach (var repository in repositories) + { + // Convert data model repositories, which have datastore connections + // and table lookups as part of the data model, to a static snapshot + // of the repository data. This will be sent to DevHome, so this is + // needs to be detached data from our data store. + devHomeRepositories.Add(new DevHomeRepository(repository)); + } + + return devHomeRepositories; + } + + private async Task PeriodicUpdate() + { + // Only update per the update interval. + // This is intended to be dynamic in the future. + if (DateTime.UtcNow - LastUpdateTime < _updateInterval) + { + return; + } + + try + { + _log.Debug("Starting account data update."); + await Update(_defaultAccountUpdateFrequency); + } + catch (Exception ex) + { + Log.Error(ex, "Update failed unexpectedly."); + } + + LastUpdateTime = DateTime.UtcNow; + return; + } + + private async Task Update(TimeSpan? olderThan) + { + var options = new RequestOptions(); + + lock (_stateLock) + { + if (UpdateInProgress) + { + _log.Information("Update is in progress, ignoring request."); + return; + } + + UpdateInProgress = true; + _cancelSource = new CancellationTokenSource(); + options.CancellationToken = _cancelSource.Token; + options.Refresh = _clearNextDataUpdate; + } + + _log.Debug("Starting account data update."); + SendUpdateEvent(this, CacheManagerUpdateKind.Started); + await DataManager.UpdateDataForAccountsAsync(olderThan ?? _defaultAccountUpdateFrequency, options, Id); + } + + private void HandleDataManagerUpdate(object? source, DataManagerUpdateEventArgs e) + { + if (e.Requestor == Id) + { + Log.Debug("DataManager update received"); + switch (e.Kind) + { + case DataManagerUpdateKind.Cache: + lock (_stateLock) + { + UpdateInProgress = false; + _pendingRefresh = false; + _clearNextDataUpdate = false; + if (e.Context.AccountsUpdated > 0) + { + // We will update this anytime we update any organization data. + LastUpdated = DateTime.UtcNow; + } + } + + _log.Information($"Accounts updated: {e.Context.AccountsUpdated} Skipped: {e.Context.AccountsSkipped} Errors: {e.Context.Errors} Elapsed: {e.Context.TimeElapsed}"); + if (e.Exception is not null) + { + _log.Warning($"First Error: {e.Exception.Message}"); + } + + SendUpdateEvent(this, CacheManagerUpdateKind.Updated); + break; + + case DataManagerUpdateKind.Cancel: + lock (_stateLock) + { + UpdateInProgress = false; + } + + _log.Information($"Operation was cancelled. Updated: {e.Context.AccountsUpdated} Skipped: {e.Context.AccountsSkipped} Errors: {e.Context.Errors} Elapsed: {e.Context.TimeElapsed}"); + SendUpdateEvent(this, CacheManagerUpdateKind.Cancel); + if (_pendingRefresh) + { + // If we were pending a refresh it was likely because a refresh caused this + // cancellation, and there is a race between the update canncellation happening + // and the subsequent update. If we get here it means that we possibly need to update. + // If the Refresh call update executes first then this one will be ignored due + // to update in progress. + _ = Update(TimeSpan.MinValue); + } + + break; + + case DataManagerUpdateKind.Error: + lock (_stateLock) + { + // Error condition means we don't know what state we are in, but we want to clear everything so retries + // can be allowed. + UpdateInProgress = false; + _pendingRefresh = false; + } + + _log.Error(e.Exception, "Update failed."); + SendUpdateEvent(this, CacheManagerUpdateKind.Error, e.Exception); + break; + } + } + } + + private void SendUpdateEvent(object? source, CacheManagerUpdateKind kind, Exception? ex = null) + { + if (OnUpdate != null) + { + _log.Debug($"Sending Update Event. Kind: {kind}"); + OnUpdate.Invoke(source, new CacheManagerUpdateEventArgs(kind, ex)); + } + } + + private void HandleDeveloperIdChange(IDeveloperIdProvider sender, IDeveloperId args) + { + try + { + // Use switch here in case new states get added later to ensure we handle all cases. + switch (DeveloperIdProvider.GetInstance().GetDeveloperIdState(args)) + { + case AuthenticationState.LoggedIn: + // New developer logging in could change some of the data, so we will do a refresh. + // This will cancel any sync in progress and start over. + _log.Information("New developer account logged in, refreshing data."); + _ = Refresh(); + return; + + case AuthenticationState.LoggedOut: + // When a developer account logs out the datastore will be re-created on next startup. + // Any data sync we do here will be discarded on next startup, but we can try to make + // the remainder of this session consistent with the current set of developer ids. + _log.Information("Developer account logged in, refreshing data."); + _ = Refresh(); + return; + } + } + catch (Exception ex) + { + _log.Error(ex, "Failed getting DeveloperId state while trying to handle DeveloperId change."); + } + } + + private DateTime GetLastUpdated() + { + var lastCacheUpdate = DataManager.GetMetaData(_cacheManagerLastUpdatedMetaDataKey); + if (string.IsNullOrEmpty(lastCacheUpdate)) + { + return DateTime.MinValue; + } + + return lastCacheUpdate.ToDateTime(); + } + + private void SetLastUpdated(DateTime time) + { + DataManager?.SetMetaData(_cacheManagerLastUpdatedMetaDataKey, time.ToDataStoreString()); + } + + private bool _disposed; // To detect redundant calls. + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + _log.Debug("Disposing of all cacheManager resources."); + + if (disposing) + { + try + { + _log.Debug("Disposing of all CacheManager resources."); + DataUpdater.Dispose(); + DataManager.Dispose(); + _cancelSource.Dispose(); + AzureDataManager.OnUpdate -= HandleDataManagerUpdate; + DeveloperIdProvider.GetInstance().Changed -= HandleDeveloperIdChange; + } + catch (Exception e) + { + _log.Error(e, "Failed disposing"); + } + } + + _disposed = true; + } + } + + // This code added to correctly implement the disposable pattern. + public void Dispose() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(true); + GC.SuppressFinalize(this); + } +} diff --git a/src/AzureExtension/DataManager/CacheManagerUpdateEventArgs.cs b/src/AzureExtension/DataManager/CacheManagerUpdateEventArgs.cs new file mode 100644 index 00000000..e8220994 --- /dev/null +++ b/src/AzureExtension/DataManager/CacheManagerUpdateEventArgs.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace DevHomeAzureExtension.DataManager; + +public delegate void CacheManagerUpdateEventHandler(object? source, CacheManagerUpdateEventArgs e); + +public enum CacheManagerUpdateKind +{ + Started, + Updated, + Cleared, + Error, + Cancel, +} + +public class CacheManagerUpdateEventArgs : EventArgs +{ + private readonly CacheManagerUpdateKind _kind; + private readonly Exception? _exception; + + public CacheManagerUpdateEventArgs(CacheManagerUpdateKind updateKind, Exception? exception = null) + { + _kind = updateKind; + _exception = exception; + } + + public CacheManagerUpdateKind Kind => _kind; + + public Exception? Exception => _exception; +} diff --git a/src/AzureExtension/DataManager/DataManagerUpdateEventArgs.cs b/src/AzureExtension/DataManager/DataManagerUpdateEventArgs.cs index 64eee483..a48f3c3a 100644 --- a/src/AzureExtension/DataManager/DataManagerUpdateEventArgs.cs +++ b/src/AzureExtension/DataManager/DataManagerUpdateEventArgs.cs @@ -10,6 +10,8 @@ public enum DataManagerUpdateKind Query, // A custom query was updated, which could be any amount of data in the datastore. PullRequest, Error, + Cache, + Cancel, } public class DataManagerUpdateEventArgs : EventArgs diff --git a/src/AzureExtension/DataManager/IAzureDataManager.cs b/src/AzureExtension/DataManager/IAzureDataManager.cs index fa8431a8..b7bc6027 100644 --- a/src/AzureExtension/DataManager/IAzureDataManager.cs +++ b/src/AzureExtension/DataManager/IAzureDataManager.cs @@ -12,6 +12,14 @@ public interface IAzureDataManager : IDisposable DateTime LastUpdated { get; } + string GetMetaData(string key); + + void SetMetaData(string key, string value); + + Task UpdateDataForAccountsAsync(RequestOptions? options = null, Guid? requestor = null); + + Task UpdateDataForAccountsAsync(TimeSpan olderThan, RequestOptions? options = null, Guid? requestor = null); + Task UpdateDataForQueryAsync(AzureUri queryUri, string developerLogin, RequestOptions? options = null, Guid? requestor = null); Task UpdateDataForQueriesAsync(IEnumerable queryUris, string developerLogin, RequestOptions? options = null, Guid? requestor = null); @@ -22,6 +30,8 @@ public interface IAzureDataManager : IDisposable Query? GetQuery(AzureUri queryUri, string developerId); + IEnumerable GetRepositories(); + // Repository name may not be unique across projects, and projects may not be unique across // organizations, so we need all three to identify the repository. PullRequests? GetPullRequests(string organization, string project, string repositoryName, string developerId, PullRequestView view); diff --git a/src/AzureExtension/DataManager/RequestOptions.cs b/src/AzureExtension/DataManager/RequestOptions.cs index 104b7659..29c16ddf 100644 --- a/src/AzureExtension/DataManager/RequestOptions.cs +++ b/src/AzureExtension/DataManager/RequestOptions.cs @@ -7,6 +7,10 @@ public class RequestOptions { public List Fields { get; set; } + public CancellationToken? CancellationToken { get; set; } + + public bool Refresh { get; set; } + public RequestOptions() { Fields = new List(); diff --git a/src/AzureExtension/DataModel/AzureDataStoreSchema.cs b/src/AzureExtension/DataModel/AzureDataStoreSchema.cs index 04b4bbd6..cf96ec94 100644 --- a/src/AzureExtension/DataModel/AzureDataStoreSchema.cs +++ b/src/AzureExtension/DataModel/AzureDataStoreSchema.cs @@ -14,7 +14,7 @@ public AzureDataStoreSchema() } // Update this anytime incompatible changes happen with a released version. - private const long SchemaVersionValue = 0x0003; + private const long SchemaVersionValue = 0x0006; private const string Metadata = @"CREATE TABLE Metadata (" + @@ -30,6 +30,7 @@ public AzureDataStoreSchema() "Name TEXT NOT NULL COLLATE NOCASE," + "InternalId TEXT NOT NULL," + "Avatar TEXT NOT NULL COLLATE NOCASE," + + "IsDeveloper INTEGER NOT NULL," + "TimeUpdated INTEGER NOT NULL" + ");" + @@ -50,12 +51,24 @@ public AzureDataStoreSchema() // Project Name can be renamed and reused per DevOps documentation, so it is not safe. "CREATE UNIQUE INDEX IDX_Project_InternalId ON Project (InternalId);"; + private const string ProjectReference = + @"CREATE TABLE ProjectReference (" + + "Id INTEGER PRIMARY KEY NOT NULL," + + "ProjectId INTEGER NOT NULL," + + "DeveloperId INTEGER NOT NULL," + + "PullRequestCount INTEGER NOT NULL" + + ");" + + + // Project references are unique by DeveloperId and ProjectId + "CREATE UNIQUE INDEX IDX_ProjectReference_ProjectIdDeveloperId ON ProjectReference (ProjectId, DeveloperId);"; + private const string Organization = @"CREATE TABLE Organization (" + "Id INTEGER PRIMARY KEY NOT NULL," + "Name TEXT NOT NULL COLLATE NOCASE," + "Connection TEXT NOT NULL COLLATE NOCASE," + - "TimeUpdated INTEGER NOT NULL" + + "TimeUpdated INTEGER NOT NULL," + + "TimeLastSync INTEGER NOT NULL" + ");" + // Connections should be unique per organization. We do not appear to have @@ -65,6 +78,31 @@ public AzureDataStoreSchema() // each connection should correspond to only one organization. "CREATE UNIQUE INDEX IDX_Organization_Connection ON Organization (Connection);"; + private const string Repository = + @"CREATE TABLE Repository (" + + "Id INTEGER PRIMARY KEY NOT NULL," + + "Name TEXT NOT NULL COLLATE NOCASE," + + "InternalId TEXT NOT NULL," + + "ProjectId INTEGER NOT NULL," + + "CloneUrl TEXT NOT NULL COLLATE NOCASE," + + "IsPrivate INTEGER NOT NULL," + + "TimeUpdated INTEGER NOT NULL" + + ");" + + + // Repository InternalId is a Guid, so by definition is unique. + "CREATE UNIQUE INDEX IDX_Repository_InternalId ON Repository (InternalId);"; + + private const string RepositoryReference = + @"CREATE TABLE RepositoryReference (" + + "Id INTEGER PRIMARY KEY NOT NULL," + + "RepositoryId INTEGER NOT NULL," + + "DeveloperId INTEGER NOT NULL," + + "PullRequestCount INTEGER NOT NULL" + + ");" + + + // Repository references are unique by DeveloperId and ProjectId + "CREATE UNIQUE INDEX IDX_RepositoryReference_RepositoryIdDeveloperId ON RepositoryReference (RepositoryId, DeveloperId);"; + private const string Query = @"CREATE TABLE Query (" + "Id INTEGER PRIMARY KEY NOT NULL," + @@ -113,14 +151,17 @@ public AzureDataStoreSchema() "CREATE UNIQUE INDEX IDX_PullRequests_ProjectIdRepositoryNameDeveloperLoginViewId ON PullRequests (ProjectId, RepositoryName, DeveloperLogin, ViewId);"; // All Sqls together. - private static readonly List _schemaSqlsValue = new() - { + private static readonly List _schemaSqlsValue = + [ Metadata, Identity, Project, + ProjectReference, Organization, + Repository, + RepositoryReference, Query, WorkItemType, PullRequests, - }; + ]; } diff --git a/src/AzureExtension/DataModel/DataObjects/Identity.cs b/src/AzureExtension/DataModel/DataObjects/Identity.cs index e61e10e5..2bb03e54 100644 --- a/src/AzureExtension/DataModel/DataObjects/Identity.cs +++ b/src/AzureExtension/DataModel/DataObjects/Identity.cs @@ -25,11 +25,11 @@ public class Identity // it reflected in the datastore. We expect identity data to change very infrequently. // Since updating an identity involves re-fetching an avatar image, we want to do this // infrequently. - private static readonly long _updateThreshold = TimeSpan.FromDays(3).Ticks; + private static readonly TimeSpan _updateThreshold = TimeSpan.FromDays(3); // Avatars may fail to download, in this case we will retry more frequently than the normal // update threshold since this can be intermittent. - private static readonly long _avatarRetryDelay = TimeSpan.FromHours(1).Ticks; + private static readonly TimeSpan _avatarRetryDelay = TimeSpan.FromHours(1); [Key] [JsonIgnore] @@ -42,6 +42,10 @@ public class Identity public string Avatar { get; set; } = string.Empty; + // Represents whether this identity is associated with a DeveloperId that is logged in. + // This is the backing database column for the IsLoggedInDeveloper property. + public long IsDeveloper { get; set; } = DataStore.NoForeignKey; + [JsonIgnore] public long TimeUpdated { get; set; } = DataStore.NoForeignKey; @@ -50,6 +54,11 @@ public class Identity [JsonIgnore] public DateTime UpdatedAt => TimeUpdated.ToDateTime(); + [Write(false)] + [Computed] + [JsonIgnore] + public bool IsLoggedInDeveloper => IsDeveloper != 0L; + public string ToJson() => JsonSerializer.Serialize(this); public override string ToString() => Name; @@ -72,7 +81,7 @@ public static string GetAvatar(VssConnection connection, Guid identity) public static Identity? FromJson(DataStore dataStore, string json) { - var log = Serilog.Log.ForContext("SourceContext", nameof(Identity)); + var log = Log.ForContext("SourceContext", nameof(Identity)); if (string.IsNullOrEmpty(json)) { return null; @@ -112,7 +121,7 @@ private static Identity CreateFromIdentityRef(IdentityRef identityRef, VssConnec InternalId = identityRef.DisplayName, Name = identityRef.DisplayName, Avatar = string.Empty, - TimeUpdated = DateTime.Now.ToDataStoreInteger(), + TimeUpdated = DateTime.UtcNow.ToDataStoreInteger(), }; } @@ -121,17 +130,36 @@ private static Identity CreateFromIdentityRef(IdentityRef identityRef, VssConnec InternalId = identityRef.Id, Name = identityRef.DisplayName, Avatar = GetAvatar(connection, new Guid(identityRef.Id)), - TimeUpdated = DateTime.Now.ToDataStoreInteger(), + TimeUpdated = DateTime.UtcNow.ToDataStoreInteger(), + }; + } + + private static Identity CreateFromIdentity(Microsoft.VisualStudio.Services.Identity.Identity identity, VssConnection connection) + { + return new Identity + { + InternalId = identity.Id.ToString(), + Name = identity.DisplayName, + Avatar = GetAvatar(connection, identity.Id), + TimeUpdated = DateTime.UtcNow.ToDataStoreInteger(), }; } - public static Identity AddOrUpdateIdentity(DataStore dataStore, Identity identity) + public static Identity AddOrUpdateIdentity(DataStore dataStore, Identity identity, bool isDeveloper = false) { // Check for existing Identity data. var existingIdentity = GetByInternalId(dataStore, identity.InternalId); if (existingIdentity is not null) { identity.Id = existingIdentity.Id; + + // If this is a developer, set to developer, but do not set to false. + // We presume not a developer unless it is explicitly set. + if (isDeveloper) + { + identity.IsDeveloper = 1; + } + dataStore.Connection!.Update(identity); return identity; } @@ -163,7 +191,8 @@ public static Identity Get(DataStore? dataStore, long id) return dataStore.Connection!.QueryFirstOrDefault(sql, param, null); } - public static Identity GetOrCreateIdentity(DataStore dataStore, IdentityRef? identityRef, VssConnection connection) + // Creation from an Azure IdentityRef object. + public static Identity GetOrCreateIdentity(DataStore dataStore, IdentityRef? identityRef, VssConnection connection, bool isDeveloper = false) { ArgumentNullException.ThrowIfNull(identityRef); @@ -181,11 +210,33 @@ public static Identity GetOrCreateIdentity(DataStore dataStore, IdentityRef? ide // We don't want to create an identity object and download a new avatar unless it needs to // be updated. In the event of an empty avatar we will retry more frequently to update it, // but not every time. - if (existing is null || ((DateTime.Now.Ticks - existing.TimeUpdated) > _updateThreshold) - || (string.IsNullOrEmpty(existing.Avatar) && ((DateTime.Now.Ticks - existing.TimeUpdated) > _avatarRetryDelay))) + if (existing is null || (isDeveloper && !existing.IsLoggedInDeveloper) || ((DateTime.UtcNow - existing.UpdatedAt) > _updateThreshold) + || (string.IsNullOrEmpty(existing.Avatar) && ((DateTime.UtcNow - existing.UpdatedAt) > _avatarRetryDelay))) { var newIdentity = CreateFromIdentityRef(identityRef, connection); - return AddOrUpdateIdentity(dataStore, newIdentity); + return AddOrUpdateIdentity(dataStore, newIdentity, isDeveloper); + } + + return existing; + } + + // Creation from an Azure Identity object. + public static Identity GetOrCreateIdentity(DataStore dataStore, Microsoft.VisualStudio.Services.Identity.Identity? identity, VssConnection connection, bool isDeveloper = false) + { + ArgumentNullException.ThrowIfNull(identity); + + Identity? existing; + existing = GetByInternalId(dataStore, identity.Id.ToString()); + + // Check for whether we need to update the record. + // We don't want to create an identity object and download a new avatar unlesss it needs to + // be updated. In the event of an empty avatar we will retry more frequently to update it, + // but not every time. + if (existing is null || (isDeveloper && !existing.IsLoggedInDeveloper) || ((DateTime.UtcNow - existing.UpdatedAt) > _updateThreshold) + || (string.IsNullOrEmpty(existing.Avatar) && ((DateTime.UtcNow - existing.UpdatedAt) > _avatarRetryDelay))) + { + var newIdentity = CreateFromIdentity(identity, connection); + return AddOrUpdateIdentity(dataStore, newIdentity, isDeveloper); } return existing; diff --git a/src/AzureExtension/DataModel/DataObjects/Organization.cs b/src/AzureExtension/DataModel/DataObjects/Organization.cs index 6b3b2a7a..f8253b2d 100644 --- a/src/AzureExtension/DataModel/DataObjects/Organization.cs +++ b/src/AzureExtension/DataModel/DataObjects/Organization.cs @@ -1,134 +1,169 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using Dapper; -using Dapper.Contrib.Extensions; -using DevHomeAzureExtension.Client; -using DevHomeAzureExtension.Helpers; - -namespace DevHomeAzureExtension.DataModel; - -[Table("Organization")] -public class Organization -{ - // This is the time between seeing a potential updated Organization record and updating it. - // This value / 2 is the average time between Organization updating their Organization data and - // having it reflected in the datastore. - private static readonly long _updateThreshold = TimeSpan.FromHours(4).Ticks; - - [Key] - public long Id { get; set; } = DataStore.NoForeignKey; - - public string Name { get; set; } = string.Empty; - - // Connection in string form. An organization may have multiple valid connection strings. - // Example: https://dev.azure.com/{organization} and https://{organization}.visualstudio.com - // We will treat the connection string as the unique identifier of the organization. - // This leaves open potential duplicate data, but not likely much. - // For this extension to function currently it does not matter. - public string Connection { get; set; } = string.Empty; - - public long TimeUpdated { get; set; } = DataStore.NoForeignKey; - - [Write(false)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Will be used in near-future changes.")] - private DataStore? DataStore { get; set; } - - [Write(false)] - [Computed] - public DateTime UpdatedAt => TimeUpdated.ToDateTime(); - - [Write(false)] - [Computed] - public Uri ConnectionUri => new(Connection); - - public override string ToString() => Name; - - private static Organization Create(Uri connection) - { - var azureUri = new AzureUri(connection); - if (!azureUri.IsValid) - { - throw new ArgumentException("Connection Uri is not valid"); - } - - return new Organization - { - Name = azureUri.Organization, - Connection = azureUri.Connection.ToString(), - TimeUpdated = DateTime.Now.ToDataStoreInteger(), - }; - } - - public static Organization AddOrUpdateOrganization(DataStore dataStore, Organization organization) - { - // Check for existing Organization data. - var existingOrganization = Get(dataStore, organization.Connection); - if (existingOrganization is not null) - { - // Many of the same Organization records will be created on a sync, and to - // avoid unnecessary updating and database operations for data that - // is extremely unlikely to have changed in any significant way, we - // will only update every UpdateThreshold amount of time. - if ((organization.TimeUpdated - existingOrganization.TimeUpdated) > _updateThreshold) - { - organization.Id = existingOrganization.Id; - dataStore.Connection!.Update(organization); - organization.DataStore = dataStore; - return organization; - } - else - { - return existingOrganization; - } - } - - // No existing pull request, add it. - organization.Id = dataStore.Connection!.Insert(organization); - organization.DataStore = dataStore; - return organization; - } - - // Direct Get always returns a non-null object for reference in other objects. - public static Organization Get(DataStore? dataStore, long id) - { - if (dataStore == null) - { - return new Organization(); - } - - var organization = dataStore.Connection!.Get(id); - if (organization != null) - { - organization.DataStore = dataStore; - } - - return organization ?? new Organization(); - } - - public static Organization? Get(DataStore dataStore, string connection) - { - // Ensure trailing slash on connection - connection = connection.Trim('/') + '/'; - - var sql = @"SELECT * FROM Organization WHERE Connection = @Connection;"; - var param = new - { - Connection = connection, - }; - - var organization = dataStore.Connection!.QueryFirstOrDefault(sql, param, null); - if (organization != null) - { - organization.DataStore = dataStore; - } - - return organization; - } - - public static Organization GetOrCreate(DataStore dataStore, Uri connection) - { - var newOrganization = Create(connection); - return AddOrUpdateOrganization(dataStore, newOrganization); - } -} +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Dapper; +using Dapper.Contrib.Extensions; +using DevHomeAzureExtension.Client; +using DevHomeAzureExtension.Helpers; +using Serilog; + +namespace DevHomeAzureExtension.DataModel; + +[Table("Organization")] +public class Organization +{ + private static readonly Lazy _logger = new(() => Log.ForContext("SourceContext", $"DataModel/{nameof(Organization)}")); + + private static readonly ILogger _log = _logger.Value; + + // This is the time between seeing a potential updated Organization record and updating it. + // This value / 2 is the average time between Organization updating their Organization data and + // having it reflected in the datastore. + private static readonly long _updateThreshold = TimeSpan.FromHours(4).Ticks; + + [Key] + public long Id { get; set; } = DataStore.NoForeignKey; + + public string Name { get; set; } = string.Empty; + + // Connection in string form. An organization may have multiple valid connection strings. + // Example: https://dev.azure.com/{organization} and https://{organization}.visualstudio.com + // We will treat the connection string as the unique identifier of the organization. + // This leaves open potential duplicate data, but not likely much. + // For this extension to function currently it does not matter. + public string Connection { get; set; } = string.Empty; + + // When this record was updated. + public long TimeUpdated { get; set; } = DataStore.NoForeignKey; + + // When all records related to this one (i.e. projects) were completely updated. + public long TimeLastSync { get; set; } = DataStore.NoForeignKey; + + [Write(false)] + [System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Will be used in near-future changes.")] + private DataStore? DataStore { get; set; } + + [Write(false)] + [Computed] + public DateTime UpdatedAt => TimeUpdated.ToDateTime(); + + [Write(false)] + [Computed] + public DateTime LastSyncAt => TimeLastSync.ToDateTime(); + + [Write(false)] + [Computed] + public Uri ConnectionUri => new(Connection); + + public override string ToString() => Name; + + public void SetSynced() + { + TimeLastSync = DateTime.UtcNow.ToDataStoreInteger(); + if (DataStore?.Connection is not null) + { + DataStore.Connection.Update(this); + } + } + + // Sets updated and sync time for all organization rows to 0, causing them to qualify for updating. + public static void ClearAllSyncData(DataStore dataStore) + { + var sql = @"UPDATE Organization SET (TimeLastSync, TimeUpdated) = ($Time, $Time);"; + var command = dataStore.Connection!.CreateCommand(); + command.CommandText = sql; + command.Parameters.AddWithValue("$Time", DataStore.NoForeignKey); + _log.Debug(DataStore.GetCommandLogMessage(sql, command)); + var rowsUpdated = command.ExecuteNonQuery(); + _log.Debug($"Updated {rowsUpdated} rows."); + } + + private static Organization Create(Uri connection) + { + var azureUri = new AzureUri(connection); + if (!azureUri.IsValid) + { + throw new ArgumentException("Connection Uri is not valid"); + } + + return new Organization + { + Name = azureUri.Organization, + Connection = azureUri.Connection.ToString(), + TimeUpdated = DateTime.UtcNow.ToDataStoreInteger(), + TimeLastSync = DateTime.MinValue.ToDataStoreInteger(), + }; + } + + public static Organization AddOrUpdateOrganization(DataStore dataStore, Organization organization) + { + // Check for existing Organization data. + var existingOrganization = Get(dataStore, organization.Connection); + if (existingOrganization is not null) + { + // Many of the same Organization records will be created on a sync, and to + // avoid unnecessary updating and database operations for data that + // is extremely unlikely to have changed in any significant way, we + // will only update every UpdateThreshold amount of time. + if ((organization.TimeUpdated - existingOrganization.TimeUpdated) > _updateThreshold) + { + organization.Id = existingOrganization.Id; + dataStore.Connection!.Update(organization); + organization.DataStore = dataStore; + return organization; + } + else + { + return existingOrganization; + } + } + + // No existing pull request, add it. + organization.Id = dataStore.Connection!.Insert(organization); + organization.DataStore = dataStore; + return organization; + } + + // Direct Get always returns a non-null object for reference in other objects. + public static Organization Get(DataStore? dataStore, long id) + { + if (dataStore == null) + { + return new Organization(); + } + + var organization = dataStore.Connection!.Get(id); + if (organization != null) + { + organization.DataStore = dataStore; + } + + return organization ?? new Organization(); + } + + public static Organization? Get(DataStore dataStore, string connection) + { + // Ensure trailing slash on connection + connection = connection.Trim('/') + '/'; + + var sql = @"SELECT * FROM Organization WHERE Connection = @Connection;"; + var param = new + { + Connection = connection, + }; + + var organization = dataStore.Connection!.QueryFirstOrDefault(sql, param, null); + if (organization != null) + { + organization.DataStore = dataStore; + } + + return organization; + } + + public static Organization GetOrCreate(DataStore dataStore, Uri connection) + { + var newOrganization = Create(connection); + return AddOrUpdateOrganization(dataStore, newOrganization); + } +} diff --git a/src/AzureExtension/DataModel/DataObjects/Project.cs b/src/AzureExtension/DataModel/DataObjects/Project.cs index 01fc24ad..4e3f898e 100644 --- a/src/AzureExtension/DataModel/DataObjects/Project.cs +++ b/src/AzureExtension/DataModel/DataObjects/Project.cs @@ -1,186 +1,232 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using Dapper; -using Dapper.Contrib.Extensions; -using DevHomeAzureExtension.Helpers; -using Microsoft.TeamFoundation.Core.WebApi; -using Serilog; - -namespace DevHomeAzureExtension.DataModel; - -[Table("Project")] -public class Project -{ - private static readonly Lazy _logger = new(() => Serilog.Log.ForContext("SourceContext", $"DataModel/{nameof(Project)}")); - - private static readonly ILogger _log = _logger.Value; - - // This is the time between seeing a potential updated Project record and updating it. - // This value / 2 is the average time between Project updating their Project data and having - // it reflected in the datastore. - private static readonly long _updateThreshold = TimeSpan.FromHours(4).Ticks; - - [Key] - public long Id { get; set; } = DataStore.NoForeignKey; - - public string Name { get; set; } = string.Empty; - - // Guid representation by ADO. - public string InternalId { get; set; } = string.Empty; - - public string Description { get; set; } = string.Empty; - - // Key in the Organization table. - public long OrganizationId { get; set; } = DataStore.NoForeignKey; - - public long TimeUpdated { get; set; } = DataStore.NoForeignKey; - - [Write(false)] - private DataStore? DataStore { get; set; } - - [Write(false)] - [Computed] - public DateTime UpdatedAt => TimeUpdated.ToDateTime(); - - [Write(false)] - [Computed] - public Organization Organization => Organization.Get(DataStore, OrganizationId); - - // Connection URI for projects always uses the Guid instead of the name, since the Guid cannot - // change for the project, but the name can change. This ensures any Uris we supply via this - // method will be consistent across project renames. - [Write(false)] - [Computed] - public Uri ConnectionUri => new($"{Organization.Get(DataStore, OrganizationId).ConnectionUri}{InternalId}/"); - - public override string ToString() => Name; - - private static Project CreateFromTeamProject(TeamProject project, long organizationId) - { - return new Project - { - InternalId = project.Id.ToString(), - Name = project.Name ?? string.Empty, - Description = project.Description ?? string.Empty, - OrganizationId = organizationId, - TimeUpdated = DateTime.Now.ToDataStoreInteger(), - }; - } - - public static Project AddOrUpdateProject(DataStore dataStore, Project project) - { - // Check for existing Project data. - var existingProject = GetByInternalId(dataStore, project.InternalId); - if (existingProject is not null) - { - // Many of the same Project records will be created on a sync, and to - // avoid unnecessary updating and database operations for data that - // is extremely unlikely to have changed in any significant way, we - // will only update every UpdateThreshold amount of time. - if ((project.TimeUpdated - existingProject.TimeUpdated) > _updateThreshold) - { - project.Id = existingProject.Id; - dataStore.Connection!.Update(project); - project.DataStore = dataStore; - return project; - } - else - { - return existingProject; - } - } - - // No existing pull request, add it. - project.Id = dataStore.Connection!.Insert(project); - project.DataStore = dataStore; - return project; - } - - // Direct Get always returns a non-null object for reference in other objects. - public static Project Get(DataStore? dataStore, long id) - { - if (dataStore == null) - { - return new Project(); - } - - var project = dataStore.Connection!.Get(id); - if (project != null) - { - project.DataStore = dataStore; - } - - return project ?? new Project(); - } - - public static Project? Get(DataStore? dataStore, string projectName, string organizationName) - { - if (dataStore == null) - { - return null; - } - - // This is not guaranteed to only return one result, such as the case of multiple - // organization records with different connections. - var sql = @"SELECT * FROM Project AS P WHERE (P.Name = @Name OR P.InternalId = @Name) AND P.OrganizationId IN (SELECT Id FROM Organization WHERE Organization.Name = @Org)"; - var param = new - { - Name = projectName, - Org = organizationName, - }; - - _log.Debug(DataStore.GetSqlLogMessage(sql, param)); - var project = dataStore.Connection!.QueryFirstOrDefault(sql, param, null); - if (project is not null) - { - project.DataStore = dataStore; - } - - return project; - } - - // Internal id should be a GUID, which is by definition unique so is sufficient for lookup. - public static Project? GetByInternalId(DataStore dataStore, string internalId) - { - var sql = @"SELECT * FROM Project WHERE InternalId = @InternalId;"; - var param = new - { - InternalId = internalId, - }; - - var project = dataStore.Connection!.QueryFirstOrDefault(sql, param, null); - if (project != null) - { - project.DataStore = dataStore; - } - - return project; - } - - // Name requires an OrganizationId for lookup because name is not guaranteed unique among - // projects. Internal Id is the best lookup. - public static Project? Get(DataStore dataStore, string name, long organizationId) - { - var sql = @"SELECT * FROM Project WHERE Name = @Name AND OrganizationId = @OrganizationId;"; - var param = new - { - Name = name, - OrganizationId = organizationId, - }; - - var project = dataStore.Connection!.QueryFirstOrDefault(sql, param, null); - if (project != null) - { - project.DataStore = dataStore; - } - - return project; - } - - public static Project GetOrCreateByTeamProject(DataStore dataStore, TeamProject project, long organizationId) - { - var newProject = CreateFromTeamProject(project, organizationId); - return AddOrUpdateProject(dataStore, newProject); - } -} +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Dapper; +using Dapper.Contrib.Extensions; +using DevHomeAzureExtension.Helpers; +using Microsoft.TeamFoundation.Core.WebApi; +using Serilog; + +namespace DevHomeAzureExtension.DataModel; + +[Table("Project")] +public class Project +{ + private static readonly Lazy _logger = new(() => Serilog.Log.ForContext("SourceContext", $"DataModel/{nameof(Project)}")); + + private static readonly ILogger _log = _logger.Value; + + // This is the time between seeing a potential updated Project record and updating it. + // This value / 2 is the average time between Project updating their Project data and having + // it reflected in the datastore. + private static readonly long _updateThreshold = TimeSpan.FromHours(4).Ticks; + + [Key] + public long Id { get; set; } = DataStore.NoForeignKey; + + public string Name { get; set; } = string.Empty; + + // Guid representation by ADO. + public string InternalId { get; set; } = string.Empty; + + public string Description { get; set; } = string.Empty; + + // Key in the Organization table. + public long OrganizationId { get; set; } = DataStore.NoForeignKey; + + // When this record was updated + public long TimeUpdated { get; set; } = DataStore.NoForeignKey; + + [Write(false)] + private DataStore? DataStore { get; set; } + + [Write(false)] + [Computed] + public DateTime UpdatedAt => TimeUpdated.ToDateTime(); + + [Write(false)] + [Computed] + public DateTime LastSyncAt => TimeUpdated.ToDateTime(); + + [Write(false)] + [Computed] + public Organization Organization => Organization.Get(DataStore, OrganizationId); + + // Connection URI for projects always uses the Guid instead of the name, since the Guid cannot + // change for the project, but the name can change. This ensures any Uris we supply via this + // method will be consistent across project renames. + [Write(false)] + [Computed] + public Uri ConnectionUri => new($"{Organization.Get(DataStore, OrganizationId).ConnectionUri}{InternalId}/"); + + public override string ToString() => Name; + + private static Project CreateFromTeamProject(TeamProject project, long organizationId) + { + return new Project + { + InternalId = project.Id.ToString(), + Name = project.Name ?? string.Empty, + Description = project.Description ?? string.Empty, + OrganizationId = organizationId, + TimeUpdated = DateTime.UtcNow.ToDataStoreInteger(), + }; + } + + private static Project CreateFromTeamProject(TeamProjectReference project, long organizationId) + { + return new Project + { + InternalId = project.Id.ToString(), + Name = project.Name ?? string.Empty, + Description = project.Description ?? string.Empty, + OrganizationId = organizationId, + TimeUpdated = DateTime.UtcNow.ToDataStoreInteger(), + }; + } + + public static Project AddOrUpdateProject(DataStore dataStore, Project project) + { + // Check for existing Project data. + var existingProject = GetByInternalId(dataStore, project.InternalId); + if (existingProject is not null) + { + // Many of the same Project records will be created on a sync, and to + // avoid unnecessary updating and database operations for data that + // is extremely unlikely to have changed in any significant way, we + // will only update every UpdateThreshold amount of time. + if ((project.TimeUpdated - existingProject.TimeUpdated) > _updateThreshold) + { + project.Id = existingProject.Id; + dataStore.Connection!.Update(project); + project.DataStore = dataStore; + return project; + } + else + { + return existingProject; + } + } + + // No existing project, add it. + project.Id = dataStore.Connection!.Insert(project); + project.DataStore = dataStore; + return project; + } + + // Direct Get always returns a non-null object for reference in other objects. + public static Project Get(DataStore? dataStore, long id) + { + if (dataStore == null) + { + return new Project(); + } + + var project = dataStore.Connection!.Get(id); + if (project != null) + { + project.DataStore = dataStore; + } + + return project ?? new Project(); + } + + public static Project? Get(DataStore? dataStore, string projectName, string organizationName) + { + if (dataStore == null) + { + return null; + } + + // This is not guaranteed to only return one result, such as the case of multiple + // organization records with different connections. + var sql = @"SELECT * FROM Project AS P WHERE (P.Name = @Name OR P.InternalId = @Name) AND P.OrganizationId IN (SELECT Id FROM Organization WHERE Organization.Name = @Org)"; + var param = new + { + Name = projectName, + Org = organizationName, + }; + + _log.Debug(DataStore.GetSqlLogMessage(sql, param)); + var project = dataStore.Connection!.QueryFirstOrDefault(sql, param, null); + if (project is not null) + { + project.DataStore = dataStore; + } + + return project; + } + + // Internal id should be a GUID, which is by definition unique so is sufficient for lookup. + public static Project? GetByInternalId(DataStore dataStore, string internalId) + { + var sql = @"SELECT * FROM Project WHERE InternalId = @InternalId;"; + var param = new + { + InternalId = internalId, + }; + + var project = dataStore.Connection!.QueryFirstOrDefault(sql, param, null); + if (project != null) + { + project.DataStore = dataStore; + } + + return project; + } + + // Name requires an OrganizationId for lookup because name is not guaranteed unique among + // projects. Internal Id is the best lookup. + public static Project? Get(DataStore dataStore, string name, long organizationId) + { + var sql = @"SELECT * FROM Project WHERE Name = @Name AND OrganizationId = @OrganizationId;"; + var param = new + { + Name = name, + OrganizationId = organizationId, + }; + + var project = dataStore.Connection!.QueryFirstOrDefault(sql, param, null); + if (project != null) + { + project.DataStore = dataStore; + } + + return project; + } + + public static IEnumerable GetAllWithReference(DataStore dataStore) + { + var sql = @"SELECT * FROM Project AS P WHERE P.Id IN (SELECT ProjectId FROM ProjectReference)"; + _log.Verbose(DataStore.GetSqlLogMessage(sql)); + var projects = dataStore.Connection!.Query(sql, null, null) ?? []; + foreach (var project in projects) + { + project.DataStore = dataStore; + } + + return projects; + } + + public static Project GetOrCreateByTeamProject(DataStore dataStore, TeamProject project, long organizationId) + { + var newProject = CreateFromTeamProject(project, organizationId); + return AddOrUpdateProject(dataStore, newProject); + } + + public static Project GetOrCreateByTeamProject(DataStore dataStore, TeamProjectReference project, long organizationId) + { + var newProject = CreateFromTeamProject(project, organizationId); + return AddOrUpdateProject(dataStore, newProject); + } + + public static void DeleteUnreferenced(DataStore dataStore) + { + // Delete any Projects that have no matching Organization. + var sql = @"DELETE FROM Project WHERE OrganizationId NOT IN (SELECT Id FROM Organization)"; + var command = dataStore.Connection!.CreateCommand(); + command.CommandText = sql; + _log.Verbose(DataStore.GetCommandLogMessage(sql, command)); + command.ExecuteNonQuery(); + } +} diff --git a/src/AzureExtension/DataModel/DataObjects/ProjectReference.cs b/src/AzureExtension/DataModel/DataObjects/ProjectReference.cs new file mode 100644 index 00000000..685796cd --- /dev/null +++ b/src/AzureExtension/DataModel/DataObjects/ProjectReference.cs @@ -0,0 +1,103 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Dapper; +using Dapper.Contrib.Extensions; +using Serilog; + +namespace DevHomeAzureExtension.DataModel; + +// This table represents direct references to a repository, such as a pull request count +// or a widget, or clone being created for that repository. This is a reference that the +// repository has some significance to the user out of the potentially thousands of repositories +// to which they may have access. +[Table("ProjectReference")] +public class ProjectReference +{ + private static readonly Lazy _logger = new(() => Log.ForContext("SourceContext", $"DataModel/{nameof(ProjectReference)}")); + + private static readonly ILogger _log = _logger.Value; + + private static readonly long _weightPullRequest = 1; + + [Key] + public long Id { get; set; } = DataStore.NoForeignKey; + + // Project table + public long ProjectId { get; set; } = DataStore.NoForeignKey; + + public long DeveloperId { get; set; } = DataStore.NoForeignKey; + + public long PullRequestCount { get; set; } = DataStore.NoForeignKey; + + [Write(false)] + [Computed] + public long Value => PullRequestCount * _weightPullRequest; + + [Write(false)] + [Computed] + public Identity Developer => Identity.Get(DataStore, DeveloperId); + + [Write(false)] + private DataStore? DataStore { get; set; } + + private static ProjectReference Create(long projectId, long developerId, long pullRequestCount) + { + return new ProjectReference + { + ProjectId = projectId, + DeveloperId = developerId, + PullRequestCount = pullRequestCount, + }; + } + + public static ProjectReference AddOrUpdate(DataStore dataStore, ProjectReference projectReference) + { + var existing = Get(dataStore, projectReference.ProjectId, projectReference.DeveloperId); + if (existing is not null) + { + projectReference.Id = existing.Id; + dataStore.Connection!.Update(projectReference); + projectReference.DataStore = dataStore; + return projectReference; + } + + projectReference.Id = dataStore.Connection!.Insert(projectReference); + projectReference.DataStore = dataStore; + return projectReference; + } + + public static ProjectReference GetOrCreate(DataStore dataStore, long projectId, long developerId, long pullRequestCount) + { + var projectReference = Create(projectId, developerId, pullRequestCount); + return AddOrUpdate(dataStore, projectReference); + } + + public static ProjectReference? Get(DataStore dataStore, long projectId, long developerId) + { + var sql = @"SELECT * FROM ProjectReference WHERE ProjectId = @ProjectId AND DeveloperId = @DeveloperId"; + var param = new + { + ProjectId = projectId, + DeveloperId = developerId, + }; + + var projectReference = dataStore.Connection!.QueryFirstOrDefault(sql, param, null); + if (projectReference != null) + { + projectReference.DataStore = dataStore; + } + + return projectReference; + } + + public static void DeleteUnreferenced(DataStore dataStore) + { + // Delete any ProjectReferences for projects that do not exist. + var sql = @"DELETE FROM ProjectReference WHERE (ProjectId NOT IN (SELECT Id FROM Project)) OR (DeveloperId NOT IN (SELECT Id FROM Identity))"; + var command = dataStore.Connection!.CreateCommand(); + command.CommandText = sql; + _log.Verbose(DataStore.GetCommandLogMessage(sql, command)); + command.ExecuteNonQuery(); + } +} diff --git a/src/AzureExtension/DataModel/DataObjects/PullRequests.cs b/src/AzureExtension/DataModel/DataObjects/PullRequests.cs index 968b74e8..9067be01 100644 --- a/src/AzureExtension/DataModel/DataObjects/PullRequests.cs +++ b/src/AzureExtension/DataModel/DataObjects/PullRequests.cs @@ -1,163 +1,163 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using Dapper; -using Dapper.Contrib.Extensions; -using DevHomeAzureExtension.Helpers; -using Serilog; -using DevId = DevHomeAzureExtension.DeveloperId; - -namespace DevHomeAzureExtension.DataModel; - -[Table("PullRequests")] -public class PullRequests -{ - private static readonly Lazy _logger = new(() => Serilog.Log.ForContext("SourceContext", $"DataModel/{nameof(PullRequests)}")); - - private static readonly ILogger _log = _logger.Value; - - // This is the time between seeing a search and updating it's TimeUpdated. - private static readonly long _updateThreshold = TimeSpan.FromMinutes(2).Ticks; - - [Key] - public long Id { get; set; } = DataStore.NoForeignKey; - - public string RepositoryName { get; set; } = string.Empty; - - // Key in Project table - public long ProjectId { get; set; } = DataStore.NoForeignKey; - - // We need developer id because pullRequests results may be different depending on the user. - public string DeveloperLogin { get; set; } = string.Empty; - - public string Results { get; set; } = string.Empty; - - public long ViewId { get; set; } = DataStore.NoForeignKey; - - public long TimeUpdated { get; set; } = DataStore.NoForeignKey; - - public override string ToString() => DeveloperLogin + "/" + RepositoryName; - - [Write(false)] - private DataStore? DataStore { get; set; } - - [Write(false)] - [Computed] - public Project Project => Project.Get(DataStore, ProjectId); - - [Write(false)] - [Computed] - public PullRequestView View => (PullRequestView)ViewId; - - [Write(false)] - [Computed] - public DevId.DeveloperId? DeveloperId => DevId.DeveloperIdProvider.GetInstance().GetDeveloperIdFromAccountIdentifier(DeveloperLogin); - - [Write(false)] - [Computed] - public DateTime UpdatedAt => TimeUpdated.ToDateTime(); - - private static PullRequests Create(string repositoryName, long projectId, string developerLogin, PullRequestView view, string pullRequests) - { - return new PullRequests - { - RepositoryName = repositoryName, - ProjectId = projectId, - DeveloperLogin = developerLogin, - Results = pullRequests, - ViewId = (long)view, - TimeUpdated = DateTime.Now.ToDataStoreInteger(), - }; - } - - private static PullRequests AddOrUpdate(DataStore dataStore, PullRequests pullRequests) - { - var existing = Get(dataStore, pullRequests.ProjectId, pullRequests.RepositoryName, pullRequests.DeveloperLogin, pullRequests.View); - if (existing is not null) - { - // Update threshold is in case there are many requests in a short period of time. - if ((pullRequests.TimeUpdated - existing.TimeUpdated) > _updateThreshold) - { - pullRequests.Id = existing.Id; - dataStore.Connection!.Update(pullRequests); - return pullRequests; - } - else - { - return existing; - } - } - - // No existing search, add it. - pullRequests.Id = dataStore.Connection!.Insert(pullRequests); - return pullRequests; - } - - // Direct Get always returns a non-null object for reference in other objects. - public static PullRequests Get(DataStore dataStore, long id) - { - if (dataStore == null) - { - return new PullRequests(); - } - - var pullRequests = dataStore.Connection!.Get(id); - if (pullRequests != null) - { - pullRequests.DataStore = dataStore; - } - - return pullRequests ?? new PullRequests(); - } - - public static PullRequests? Get(DataStore dataStore, long projectId, string repositoryName, string developerLogin, PullRequestView view) - { - var sql = @"SELECT * FROM PullRequests WHERE ProjectId = @ProjectId AND RepositoryName = @RepositoryName AND DeveloperLogin = @DeveloperLogin AND ViewId = @ViewId;"; - var param = new - { - ProjectId = projectId, - RepositoryName = repositoryName, - DeveloperLogin = developerLogin, - ViewId = (long)view, - }; - - var pullRequests = dataStore.Connection!.QueryFirstOrDefault(sql, param, null); - if (pullRequests is not null) - { - pullRequests.DataStore = dataStore; - } - - return pullRequests; - } - - // DeveloperPullRequests is unique on developerId, repositoryName, project, and organization. - public static PullRequests? Get(DataStore dataStore, string organizationName, string projectName, string repositoryName, string developerLogin, PullRequestView view) - { - // Since this also requires organization information and project is referenced by Id, we must first look up the project. - var project = Project.Get(dataStore, projectName, organizationName); - if (project == null) - { - return null; - } - - return Get(dataStore, project.Id, repositoryName, developerLogin, view); - } - - public static PullRequests GetOrCreate(DataStore dataStore, string repositoryName, long projectId, string developerId, PullRequestView view, string pullRequests) - { - var newDeveloperPullRequests = Create(repositoryName, projectId, developerId, view, pullRequests); - return AddOrUpdate(dataStore, newDeveloperPullRequests); - } - - public static void DeleteBefore(DataStore dataStore, DateTime date) - { - // Delete queries older than the date listed. - var sql = @"DELETE FROM PullRequests WHERE TimeUpdated < $Time;"; - var command = dataStore.Connection!.CreateCommand(); - command.CommandText = sql; - command.Parameters.AddWithValue("$Time", date.ToDataStoreInteger()); - _log.Debug(DataStore.GetCommandLogMessage(sql, command)); - var rowsDeleted = command.ExecuteNonQuery(); - _log.Debug(DataStore.GetDeletedLogMessage(rowsDeleted)); - } -} +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Dapper; +using Dapper.Contrib.Extensions; +using DevHomeAzureExtension.Helpers; +using Serilog; +using DevId = DevHomeAzureExtension.DeveloperId; + +namespace DevHomeAzureExtension.DataModel; + +[Table("PullRequests")] +public class PullRequests +{ + private static readonly Lazy _logger = new(() => Serilog.Log.ForContext("SourceContext", $"DataModel/{nameof(PullRequests)}")); + + private static readonly ILogger _log = _logger.Value; + + // This is the time between seeing a search and updating it's TimeUpdated. + private static readonly long _updateThreshold = TimeSpan.FromMinutes(2).Ticks; + + [Key] + public long Id { get; set; } = DataStore.NoForeignKey; + + public string RepositoryName { get; set; } = string.Empty; + + // Key in Project table + public long ProjectId { get; set; } = DataStore.NoForeignKey; + + // We need developer id because pullRequests results may be different depending on the user. + public string DeveloperLogin { get; set; } = string.Empty; + + public string Results { get; set; } = string.Empty; + + public long ViewId { get; set; } = DataStore.NoForeignKey; + + public long TimeUpdated { get; set; } = DataStore.NoForeignKey; + + public override string ToString() => DeveloperLogin + "/" + RepositoryName; + + [Write(false)] + private DataStore? DataStore { get; set; } + + [Write(false)] + [Computed] + public Project Project => Project.Get(DataStore, ProjectId); + + [Write(false)] + [Computed] + public PullRequestView View => (PullRequestView)ViewId; + + [Write(false)] + [Computed] + public DevId.DeveloperId? DeveloperId => DevId.DeveloperIdProvider.GetInstance().GetDeveloperIdFromAccountIdentifier(DeveloperLogin); + + [Write(false)] + [Computed] + public DateTime UpdatedAt => TimeUpdated.ToDateTime(); + + private static PullRequests Create(string repositoryName, long projectId, string developerLogin, PullRequestView view, string pullRequests) + { + return new PullRequests + { + RepositoryName = repositoryName, + ProjectId = projectId, + DeveloperLogin = developerLogin, + Results = pullRequests, + ViewId = (long)view, + TimeUpdated = DateTime.UtcNow.ToDataStoreInteger(), + }; + } + + private static PullRequests AddOrUpdate(DataStore dataStore, PullRequests pullRequests) + { + var existing = Get(dataStore, pullRequests.ProjectId, pullRequests.RepositoryName, pullRequests.DeveloperLogin, pullRequests.View); + if (existing is not null) + { + // Update threshold is in case there are many requests in a short period of time. + if ((pullRequests.TimeUpdated - existing.TimeUpdated) > _updateThreshold) + { + pullRequests.Id = existing.Id; + dataStore.Connection!.Update(pullRequests); + return pullRequests; + } + else + { + return existing; + } + } + + // No existing search, add it. + pullRequests.Id = dataStore.Connection!.Insert(pullRequests); + return pullRequests; + } + + // Direct Get always returns a non-null object for reference in other objects. + public static PullRequests Get(DataStore dataStore, long id) + { + if (dataStore == null) + { + return new PullRequests(); + } + + var pullRequests = dataStore.Connection!.Get(id); + if (pullRequests != null) + { + pullRequests.DataStore = dataStore; + } + + return pullRequests ?? new PullRequests(); + } + + public static PullRequests? Get(DataStore dataStore, long projectId, string repositoryName, string developerLogin, PullRequestView view) + { + var sql = @"SELECT * FROM PullRequests WHERE ProjectId = @ProjectId AND RepositoryName = @RepositoryName AND DeveloperLogin = @DeveloperLogin AND ViewId = @ViewId;"; + var param = new + { + ProjectId = projectId, + RepositoryName = repositoryName, + DeveloperLogin = developerLogin, + ViewId = (long)view, + }; + + var pullRequests = dataStore.Connection!.QueryFirstOrDefault(sql, param, null); + if (pullRequests is not null) + { + pullRequests.DataStore = dataStore; + } + + return pullRequests; + } + + // DeveloperPullRequests is unique on developerId, repositoryName, project, and organization. + public static PullRequests? Get(DataStore dataStore, string organizationName, string projectName, string repositoryName, string developerLogin, PullRequestView view) + { + // Since this also requires organization information and project is referenced by Id, we must first look up the project. + var project = Project.Get(dataStore, projectName, organizationName); + if (project == null) + { + return null; + } + + return Get(dataStore, project.Id, repositoryName, developerLogin, view); + } + + public static PullRequests GetOrCreate(DataStore dataStore, string repositoryName, long projectId, string developerId, PullRequestView view, string pullRequests) + { + var newDeveloperPullRequests = Create(repositoryName, projectId, developerId, view, pullRequests); + return AddOrUpdate(dataStore, newDeveloperPullRequests); + } + + public static void DeleteBefore(DataStore dataStore, DateTime date) + { + // Delete queries older than the date listed. + var sql = @"DELETE FROM PullRequests WHERE TimeUpdated < $Time;"; + var command = dataStore.Connection!.CreateCommand(); + command.CommandText = sql; + command.Parameters.AddWithValue("$Time", date.ToDataStoreInteger()); + _log.Debug(DataStore.GetCommandLogMessage(sql, command)); + var rowsDeleted = command.ExecuteNonQuery(); + _log.Debug(DataStore.GetDeletedLogMessage(rowsDeleted)); + } +} diff --git a/src/AzureExtension/DataModel/DataObjects/Query.cs b/src/AzureExtension/DataModel/DataObjects/Query.cs index 9ffe5307..1cfe522a 100644 --- a/src/AzureExtension/DataModel/DataObjects/Query.cs +++ b/src/AzureExtension/DataModel/DataObjects/Query.cs @@ -1,150 +1,150 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using Dapper; -using Dapper.Contrib.Extensions; -using DevHomeAzureExtension.Helpers; -using Serilog; -using DevId = DevHomeAzureExtension.DeveloperId; - -namespace DevHomeAzureExtension.DataModel; - -[Table("Query")] -public class Query -{ - private static readonly Lazy _logger = new(() => Serilog.Log.ForContext("SourceContext", $"DataModel/{nameof(Query)}")); - - private static readonly ILogger _log = _logger.Value; - - // This is the time between seeing a search and updating it's TimeUpdated. - private static readonly long _updateThreshold = TimeSpan.FromMinutes(2).Ticks; - - [Key] - public long Id { get; set; } = DataStore.NoForeignKey; - - // Guid - public string QueryId { get; set; } = string.Empty; - - public string DisplayName { get; set; } = string.Empty; - - // Key in Project table - public long ProjectId { get; set; } = DataStore.NoForeignKey; - - // We need developer id because query results may be different depending on the user. - public string DeveloperLogin { get; set; } = string.Empty; - - // Query results will be a JSON string. - public string QueryResults { get; set; } = string.Empty; - - // Result count for quick access. - public long QueryResultCount { get; set; } = DataStore.NoForeignKey; - - public long TimeUpdated { get; set; } = DataStore.NoForeignKey; - - public override string ToString() => QueryId; - - [Write(false)] - private DataStore? DataStore { get; set; } - - [Write(false)] - [Computed] - public Project Project => Project.Get(DataStore, ProjectId); - - [Write(false)] - [Computed] - public DevId.DeveloperId? DeveloperId => DevId.DeveloperIdProvider.GetInstance().GetDeveloperIdFromAccountIdentifier(DeveloperLogin); - - [Write(false)] - [Computed] - public DateTime UpdatedAt => TimeUpdated.ToDateTime(); - - private static Query Create(string queryId, long projectId, string developerLogin, string displayName, string queryResults, long queryResultCount) - { - return new Query - { - QueryId = queryId, - ProjectId = projectId, - DeveloperLogin = developerLogin, - DisplayName = displayName, - QueryResults = queryResults, - QueryResultCount = queryResultCount, - TimeUpdated = DateTime.Now.ToDataStoreInteger(), - }; - } - - private static Query AddOrUpdate(DataStore dataStore, Query query) - { - var existing = Get(dataStore, query.QueryId, query.DeveloperLogin); - if (existing is not null) - { - if ((query.TimeUpdated - existing.TimeUpdated) > _updateThreshold) - { - query.Id = existing.Id; - dataStore.Connection!.Update(query); - return query; - } - else - { - return existing; - } - } - - // No existing search, add it. - query.Id = dataStore.Connection!.Insert(query); - return query; - } - - // Direct Get always returns a non-null object for reference in other objects. - public static Query Get(DataStore dataStore, long id) - { - if (dataStore == null) - { - return new Query(); - } - - var query = dataStore.Connection!.Get(id); - if (query != null) - { - query.DataStore = dataStore; - } - - return query ?? new Query(); - } - - // Query is unique on queryId and developerId - public static Query? Get(DataStore dataStore, string queryId, string developerLogin) - { - var sql = @"SELECT * FROM Query WHERE QueryId = @QueryId AND DeveloperLogin = @DeveloperLogin;"; - var param = new - { - QueryId = queryId, - DeveloperLogin = developerLogin, - }; - - var query = dataStore.Connection!.QueryFirstOrDefault(sql, param, null); - if (query is not null) - { - query.DataStore = dataStore; - } - - return query; - } - - public static Query GetOrCreate(DataStore dataStore, string queryId, long projectId, string developerId, string displayName, string queryResults, long queryResultCount) - { - var newQuery = Create(queryId, projectId, developerId, displayName, queryResults, queryResultCount); - return AddOrUpdate(dataStore, newQuery); - } - - public static void DeleteBefore(DataStore dataStore, DateTime date) - { - // Delete queries older than the date listed. - var sql = @"DELETE FROM Query WHERE TimeUpdated < $Time;"; - var command = dataStore.Connection!.CreateCommand(); - command.CommandText = sql; - command.Parameters.AddWithValue("$Time", date.ToDataStoreInteger()); - _log.Debug(DataStore.GetCommandLogMessage(sql, command)); - var rowsDeleted = command.ExecuteNonQuery(); - _log.Debug(DataStore.GetDeletedLogMessage(rowsDeleted)); - } -} +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Dapper; +using Dapper.Contrib.Extensions; +using DevHomeAzureExtension.Helpers; +using Serilog; +using DevId = DevHomeAzureExtension.DeveloperId; + +namespace DevHomeAzureExtension.DataModel; + +[Table("Query")] +public class Query +{ + private static readonly Lazy _logger = new(() => Serilog.Log.ForContext("SourceContext", $"DataModel/{nameof(Query)}")); + + private static readonly ILogger _log = _logger.Value; + + // This is the time between seeing a search and updating it's TimeUpdated. + private static readonly long _updateThreshold = TimeSpan.FromMinutes(2).Ticks; + + [Key] + public long Id { get; set; } = DataStore.NoForeignKey; + + // Guid + public string QueryId { get; set; } = string.Empty; + + public string DisplayName { get; set; } = string.Empty; + + // Key in Project table + public long ProjectId { get; set; } = DataStore.NoForeignKey; + + // We need developer id because query results may be different depending on the user. + public string DeveloperLogin { get; set; } = string.Empty; + + // Query results will be a JSON string. + public string QueryResults { get; set; } = string.Empty; + + // Result count for quick access. + public long QueryResultCount { get; set; } = DataStore.NoForeignKey; + + public long TimeUpdated { get; set; } = DataStore.NoForeignKey; + + public override string ToString() => QueryId; + + [Write(false)] + private DataStore? DataStore { get; set; } + + [Write(false)] + [Computed] + public Project Project => Project.Get(DataStore, ProjectId); + + [Write(false)] + [Computed] + public DevId.DeveloperId? DeveloperId => DevId.DeveloperIdProvider.GetInstance().GetDeveloperIdFromAccountIdentifier(DeveloperLogin); + + [Write(false)] + [Computed] + public DateTime UpdatedAt => TimeUpdated.ToDateTime(); + + private static Query Create(string queryId, long projectId, string developerLogin, string displayName, string queryResults, long queryResultCount) + { + return new Query + { + QueryId = queryId, + ProjectId = projectId, + DeveloperLogin = developerLogin, + DisplayName = displayName, + QueryResults = queryResults, + QueryResultCount = queryResultCount, + TimeUpdated = DateTime.UtcNow.ToDataStoreInteger(), + }; + } + + private static Query AddOrUpdate(DataStore dataStore, Query query) + { + var existing = Get(dataStore, query.QueryId, query.DeveloperLogin); + if (existing is not null) + { + if ((query.TimeUpdated - existing.TimeUpdated) > _updateThreshold) + { + query.Id = existing.Id; + dataStore.Connection!.Update(query); + return query; + } + else + { + return existing; + } + } + + // No existing search, add it. + query.Id = dataStore.Connection!.Insert(query); + return query; + } + + // Direct Get always returns a non-null object for reference in other objects. + public static Query Get(DataStore dataStore, long id) + { + if (dataStore == null) + { + return new Query(); + } + + var query = dataStore.Connection!.Get(id); + if (query != null) + { + query.DataStore = dataStore; + } + + return query ?? new Query(); + } + + // Query is unique on queryId and developerId + public static Query? Get(DataStore dataStore, string queryId, string developerLogin) + { + var sql = @"SELECT * FROM Query WHERE QueryId = @QueryId AND DeveloperLogin = @DeveloperLogin;"; + var param = new + { + QueryId = queryId, + DeveloperLogin = developerLogin, + }; + + var query = dataStore.Connection!.QueryFirstOrDefault(sql, param, null); + if (query is not null) + { + query.DataStore = dataStore; + } + + return query; + } + + public static Query GetOrCreate(DataStore dataStore, string queryId, long projectId, string developerId, string displayName, string queryResults, long queryResultCount) + { + var newQuery = Create(queryId, projectId, developerId, displayName, queryResults, queryResultCount); + return AddOrUpdate(dataStore, newQuery); + } + + public static void DeleteBefore(DataStore dataStore, DateTime date) + { + // Delete queries older than the date listed. + var sql = @"DELETE FROM Query WHERE TimeUpdated < $Time;"; + var command = dataStore.Connection!.CreateCommand(); + command.CommandText = sql; + command.Parameters.AddWithValue("$Time", date.ToDataStoreInteger()); + _log.Debug(DataStore.GetCommandLogMessage(sql, command)); + var rowsDeleted = command.ExecuteNonQuery(); + _log.Debug(DataStore.GetDeletedLogMessage(rowsDeleted)); + } +} diff --git a/src/AzureExtension/DataModel/DataObjects/Repository.cs b/src/AzureExtension/DataModel/DataObjects/Repository.cs new file mode 100644 index 00000000..90b6f950 --- /dev/null +++ b/src/AzureExtension/DataModel/DataObjects/Repository.cs @@ -0,0 +1,190 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Dapper; +using Dapper.Contrib.Extensions; +using DevHomeAzureExtension.Client; +using DevHomeAzureExtension.Helpers; +using Microsoft.TeamFoundation.SourceControl.WebApi; +using Microsoft.Windows.DevHome.SDK; +using Serilog; + +namespace DevHomeAzureExtension.DataModel; + +[Table("Repository")] +public class Repository +{ + private static readonly Lazy _logger = new(() => Serilog.Log.ForContext("SourceContext", $"DataModel/{nameof(Repository)}")); + + private static readonly ILogger _log = _logger.Value; + + [Key] + public long Id { get; set; } = DataStore.NoForeignKey; + + public string Name { get; set; } = string.Empty; + + // Guid representation by ADO. + public string InternalId { get; set; } = string.Empty; + + // Key in the Project table. + public long ProjectId { get; set; } = DataStore.NoForeignKey; + + public long IsPrivate { get; set; } = DataStore.NoForeignKey; + + public string CloneUrl { get; set; } = string.Empty; + + // When this record was updated + public long TimeUpdated { get; set; } = DataStore.NoForeignKey; + + [Write(false)] + private DataStore? DataStore { get; set; } + + [Write(false)] + [Computed] + public DateTime UpdatedAt => TimeUpdated.ToDateTime(); + + [Write(false)] + [Computed] + public bool Private => IsPrivate != 0L; + + [Write(false)] + [Computed] + public AzureUri Clone => new(CloneUrl); + + [Write(false)] + [Computed] + public Project Project => Project.Get(DataStore, ProjectId); + + [Write(false)] + [Computed] + public long ReferenceValue + { + get + { + if (DataStore is null) + { + return 0; + } + else + { + return RepositoryReference.GetRepositoryValue(DataStore, Id); + } + } + } + + public override string ToString() => Name; + + private static Repository Create(GitRepository gitRepository, long projectId) + { + return new Repository + { + InternalId = gitRepository.Id.ToString(), + Name = gitRepository.Name, + CloneUrl = gitRepository.WebUrl, + IsPrivate = gitRepository.ProjectReference.Visibility == Microsoft.TeamFoundation.Core.WebApi.ProjectVisibility.Private ? 1L : 0L, + ProjectId = projectId, + TimeUpdated = DateTime.UtcNow.ToDataStoreInteger(), + }; + } + + public static Repository AddOrUpdate(DataStore dataStore, Repository repository) + { + // Check for existing Repository data. + var existingRepository = GetByInternalId(dataStore, repository.InternalId); + if (existingRepository is not null) + { + repository.Id = existingRepository.Id; + dataStore.Connection!.Update(repository); + repository.DataStore = dataStore; + return repository; + } + + // No existing repository, add it. + repository.Id = dataStore.Connection!.Insert(repository); + repository.DataStore = dataStore; + return repository; + } + + public static Repository GetOrCreate(DataStore dataStore, GitRepository gitRepository, long projectId) + { + // This will always update the repository record every time we see it, which means + // UpdatedAt is effectively the last time we observed the Repository existing. + var repository = Create(gitRepository, projectId); + return AddOrUpdate(dataStore, repository); + } + + // Direct Get always returns a non-null object for reference in other objects. + public static Repository Get(DataStore? dataStore, long id) + { + if (dataStore == null) + { + return new Repository(); + } + + var repository = dataStore.Connection!.Get(id); + if (repository != null) + { + repository.DataStore = dataStore; + } + + return repository ?? new Repository(); + } + + public static IEnumerable GetAll(DataStore dataStore) + { + var repositories = dataStore.Connection!.GetAll() ?? []; + foreach (var repository in repositories) + { + repository.DataStore = dataStore; + } + + _log.Information("Getting all repositories."); + return repositories; + } + + // Internal id should be a GUID, which is by definition unique so is sufficient for lookup. + public static Repository? GetByInternalId(DataStore dataStore, string internalId) + { + var sql = @"SELECT * FROM Repository WHERE InternalId = @InternalId;"; + var param = new + { + InternalId = internalId, + }; + + var repository = dataStore.Connection!.QueryFirstOrDefault(sql, param, null); + if (repository != null) + { + repository.DataStore = dataStore; + } + + return repository; + } + + public static Repository? Get(DataStore dataStore, GitRepository repository) + { + return GetByInternalId(dataStore, repository.Id.ToString()); + } + + public static IEnumerable GetAllWithReference(DataStore dataStore) + { + var sql = @"SELECT * FROM Repository AS R WHERE R.Id IN (SELECT RepositoryId FROM RepositoryReference)"; + _log.Verbose(DataStore.GetSqlLogMessage(sql)); + var repositories = dataStore.Connection!.Query(sql) ?? []; + foreach (var repository in repositories) + { + repository.DataStore = dataStore; + } + + return repositories; + } + + public static void DeleteUnreferenced(DataStore dataStore) + { + // Delete any Repositories that have no matching Project. + var sql = @"DELETE FROM Repository WHERE ProjectId NOT IN (SELECT Id FROM Project)"; + var command = dataStore.Connection!.CreateCommand(); + command.CommandText = sql; + _log.Verbose(DataStore.GetCommandLogMessage(sql, command)); + command.ExecuteNonQuery(); + } +} diff --git a/src/AzureExtension/DataModel/DataObjects/RepositoryReference.cs b/src/AzureExtension/DataModel/DataObjects/RepositoryReference.cs new file mode 100644 index 00000000..a40294bf --- /dev/null +++ b/src/AzureExtension/DataModel/DataObjects/RepositoryReference.cs @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Dapper; +using Dapper.Contrib.Extensions; +using Microsoft.TeamFoundation.SourceControl.WebApi; +using Serilog; + +namespace DevHomeAzureExtension.DataModel; + +// This table represents direct references to a repository, such as a pull request count +// or a widget, or clone being created for that repository. This is a reference that the +// repository has some significance to the user out of the potentially thousands of repositories +// to which they may have access. +[Table("RepositoryReference")] +public class RepositoryReference +{ + private static readonly Lazy _log = new(() => Serilog.Log.ForContext("SourceContext", $"DataModel/{nameof(RepositoryReference)}")); + + private static readonly ILogger Log = _log.Value; + + private static readonly long WeightPullRequest = 1; + + [Key] + public long Id { get; set; } = DataStore.NoForeignKey; + + // Repository table + public long RepositoryId { get; set; } = DataStore.NoForeignKey; + + public long DeveloperId { get; set; } = DataStore.NoForeignKey; + + public long PullRequestCount { get; set; } = DataStore.NoForeignKey; + + [Write(false)] + [Computed] + public long Value => PullRequestCount * WeightPullRequest; + + [Write(false)] + [Computed] + public Identity Developer => Identity.Get(DataStore, DeveloperId); + + [Write(false)] + private DataStore? DataStore { get; set; } + + public static long GetRepositoryValue(DataStore dataStore, long repositoryId) + { + // TODO: Sum up values of all references to this repository across users. + return 0; + } + + private static RepositoryReference Create(long repositoryId, long developerId, long pullRequestCount) + { + return new RepositoryReference + { + RepositoryId = repositoryId, + DeveloperId = developerId, + PullRequestCount = pullRequestCount, + }; + } + + public static RepositoryReference AddOrUpdate(DataStore dataStore, RepositoryReference repositoryReference) + { + var existing = Get(dataStore, repositoryReference.RepositoryId, repositoryReference.DeveloperId); + if (existing is not null) + { + repositoryReference.Id = existing.Id; + dataStore.Connection!.Update(repositoryReference); + repositoryReference.DataStore = dataStore; + return repositoryReference; + } + + repositoryReference.Id = dataStore.Connection!.Insert(repositoryReference); + repositoryReference.DataStore = dataStore; + return repositoryReference; + } + + public static RepositoryReference GetOrCreate(DataStore dataStore, long repositoryId, long developerId, long pullRequestCount) + { + var repositoryReference = Create(repositoryId, developerId, pullRequestCount); + return AddOrUpdate(dataStore, repositoryReference); + } + + // Get by Repository row Id + public static RepositoryReference? Get(DataStore dataStore, long repositoryId, long developerId) + { + var sql = @"SELECT * FROM RepositoryReference WHERE RepositoryId = @RepositoryId AND DeveloperId = @DeveloperId"; + var param = new + { + RepositoryId = repositoryId, + DeveloperId = developerId, + }; + + var repositoryReference = dataStore.Connection!.QueryFirstOrDefault(sql, param, null); + if (repositoryReference != null) + { + repositoryReference.DataStore = dataStore; + } + + return repositoryReference; + } + + public static void DeleteUnreferenced(DataStore dataStore) + { + // Delete any RepositoryReferences for repositories that do not exist. + var sql = @"DELETE FROM RepositoryReference WHERE (RepositoryId NOT IN (SELECT Id FROM Repository)) OR (DeveloperId NOT IN (SELECT Id FROM Identity))"; + var command = dataStore.Connection!.CreateCommand(); + command.CommandText = sql; + Log.Verbose(DataStore.GetCommandLogMessage(sql, command)); + command.ExecuteNonQuery(); + } +} diff --git a/src/AzureExtension/DataModel/DataObjects/WorkItemType.cs b/src/AzureExtension/DataModel/DataObjects/WorkItemType.cs index a34083f6..9fab20ba 100644 --- a/src/AzureExtension/DataModel/DataObjects/WorkItemType.cs +++ b/src/AzureExtension/DataModel/DataObjects/WorkItemType.cs @@ -96,7 +96,7 @@ private static WorkItemType CreateFromTeamWorkItemType(TeamWorkItemType workItem Icon = workItemType.Icon.Url.ToString() ?? string.Empty, Color = workItemType.Color ?? string.Empty, ProjectId = projectId, - TimeUpdated = DateTime.Now.ToDataStoreInteger(), + TimeUpdated = DateTime.UtcNow.ToDataStoreInteger(), }; } diff --git a/src/AzureExtension/Helpers/FileHelper.cs b/src/AzureExtension/Helpers/FileHelper.cs new file mode 100644 index 00000000..5e087fe3 --- /dev/null +++ b/src/AzureExtension/Helpers/FileHelper.cs @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Diagnostics; +using System.Text; +using Newtonsoft.Json; +using Serilog; + +namespace DevHomeAzureExtension.Helpers; + +public static class FileHelper +{ +#pragma warning disable CS8603 // Possible null reference return. + public static T Read(string folderPath, string fileName) + { + var path = Path.Combine(folderPath, fileName); + if (File.Exists(path)) + { + var json = File.ReadAllText(path); + return JsonConvert.DeserializeObject(json); + } + + return default; + } +#pragma warning restore CS8603 // Possible null reference return. + + public static void Save(string folderPath, string fileName, T content) + { + if (!Directory.Exists(folderPath)) + { + Directory.CreateDirectory(folderPath); + } + + var fileContent = JsonConvert.SerializeObject(content); + File.WriteAllText(Path.Combine(folderPath, fileName), fileContent, Encoding.UTF8); + } + + public static void Delete(string folderPath, string fileName) + { + if (!string.IsNullOrEmpty(fileName) && File.Exists(Path.Combine(folderPath, fileName))) + { + File.Delete(Path.Combine(folderPath, fileName)); + } + } + + public static void OpenLogsLocation() + { + var log = Log.ForContext("SourceContext", "OpenLogs"); + try + { + var logLocation = Environment.GetEnvironmentVariable("DEVHOME_LOGS_ROOT"); + log.Information($"Opening logs at: {logLocation}"); + Process.Start("explorer.exe", $"{logLocation}"); + } + catch (Exception e) + { + log.Error(e, $"Error opening log location"); + } + } +} diff --git a/src/AzureExtension/Helpers/Json.cs b/src/AzureExtension/Helpers/Json.cs new file mode 100644 index 00000000..c895f2fc --- /dev/null +++ b/src/AzureExtension/Helpers/Json.cs @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Text.Json; +using System.Text.Json.Serialization; +using Newtonsoft.Json; + +namespace DevHomeAzureExtension.Helpers; + +public static class Json +{ + public static async Task ToObjectAsync(string value) + { + if (typeof(T) == typeof(bool)) + { + return (T)(object)bool.Parse(value); + } + + return await Task.Run(() => + { + return JsonConvert.DeserializeObject(value)!; + }); + } + + public static async Task StringifyAsync(T value) + { + if (typeof(T) == typeof(bool)) + { + return value!.ToString()!.ToLowerInvariant(); + } + + return await Task.Run(() => + { + return JsonConvert.SerializeObject(value); + }); + } + + public static string Stringify(T value) + { + if (typeof(T) == typeof(bool)) + { + return value!.ToString()!.ToLowerInvariant(); + } + +#pragma warning disable CA1869 // Cache and reuse 'JsonSerializerOptions' instances + return System.Text.Json.JsonSerializer.Serialize( + value, + new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true, + DefaultIgnoreCondition = JsonIgnoreCondition.Never, + IncludeFields = true, + }); +#pragma warning restore CA1869 // Cache and reuse 'JsonSerializerOptions' instances + } + + public static T? ToObject(string json) + { + if (typeof(T) == typeof(bool)) + { + return (T)(object)bool.Parse(json); + } + +#pragma warning disable CA1869 // Cache and reuse 'JsonSerializerOptions' instances + return System.Text.Json.JsonSerializer.Deserialize(json, new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true, + DefaultIgnoreCondition = JsonIgnoreCondition.Never, + IncludeFields = true, + }); +#pragma warning restore CA1869 // Cache and reuse 'JsonSerializerOptions' instances + } +} diff --git a/src/AzureExtension/Helpers/LocalSettings.cs b/src/AzureExtension/Helpers/LocalSettings.cs new file mode 100644 index 00000000..1092df6d --- /dev/null +++ b/src/AzureExtension/Helpers/LocalSettings.cs @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Windows.Storage; + +namespace DevHomeAzureExtension.Helpers; + +public static class LocalSettings +{ + private static readonly string _applicationDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "DevHome/ApplicationData"); + private static readonly string _localSettingsFile = "LocalSettings.json"; + +#pragma warning disable CA1859 // Use concrete types when possible for improved performance + private static IDictionary? _settings; +#pragma warning restore CA1859 // Use concrete types when possible for improved performance + + private static async Task InitializeAsync() + { + if (_settings == null) + { + if (RuntimeHelper.IsMSIX) + { + _settings = new Dictionary(); + } + else + { + _settings = await Task.Run(() => FileHelper.Read>(_applicationDataFolder, _localSettingsFile)) ?? new Dictionary(); + } + } + } + + public static async Task ReadSettingAsync(string key) + { + await InitializeAsync(); + + if (_settings != null) + { + if (_settings.TryGetValue(key, out var obj)) + { + return await Json.ToObjectAsync((string)obj); + } + else + { + if (RuntimeHelper.IsMSIX) + { + if (ApplicationData.Current.LocalSettings.Values.TryGetValue(key, out var obj2)) + { + _settings![key] = obj2; + return await Json.ToObjectAsync((string)obj2); + } + } + } + } + + return default; + } + + public static async Task SaveSettingAsync(string key, T value) + { + await InitializeAsync(); + + if (_settings != null) + { + _settings![key] = await Json.StringifyAsync(value!); + + if (RuntimeHelper.IsMSIX) + { + ApplicationData.Current.LocalSettings.Values[key] = _settings![key]; + } + else + { + await Task.Run(() => FileHelper.Save(_applicationDataFolder, _localSettingsFile, _settings)); + } + } + } +} diff --git a/src/AzureExtension/Helpers/Resources.cs b/src/AzureExtension/Helpers/Resources.cs index 89d6254e..cd87cea2 100644 --- a/src/AzureExtension/Helpers/Resources.cs +++ b/src/AzureExtension/Helpers/Resources.cs @@ -63,7 +63,7 @@ public static string GetResource(string key, params object[] args) // such as a JSON string with resource identifiers embedded. public static string ReplaceIdentifiers(string str, string[] resourceIdentifiers, ILogger? log = null) { - var start = DateTime.Now; + var start = DateTime.UtcNow; foreach (var identifier in resourceIdentifiers) { // What is faster, String.Replace, RegEx, or StringBuilder.Replace? It is String.Replace(). @@ -72,7 +72,7 @@ public static string ReplaceIdentifiers(string str, string[] resourceIdentifiers str = str.Replace($"%{identifier}%", resourceString); } - var elapsed = DateTime.Now - start; + var elapsed = DateTime.UtcNow - start; log?.Debug($"Replaced identifiers in {elapsed.TotalMilliseconds}ms"); return str; } @@ -80,8 +80,8 @@ public static string ReplaceIdentifiers(string str, string[] resourceIdentifiers // These are all the string identifiers that appear in widgets. public static string[] GetWidgetResourceIdentifiers() { - return new string[] - { + return + [ "Extension_Name/Azure", "Widget_Template/Loading", "Widget_Template/Updated", @@ -119,7 +119,7 @@ public static string[] GetWidgetResourceIdentifiers() "Widget_Template/EmptyWorkItems", "Widget_Template/NotShownItems", "Widget_Template/ContentLoading", - }; + ]; } /// diff --git a/src/AzureExtension/Helpers/RuntimeHelper.cs b/src/AzureExtension/Helpers/RuntimeHelper.cs new file mode 100644 index 00000000..4e6214c9 --- /dev/null +++ b/src/AzureExtension/Helpers/RuntimeHelper.cs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Windows.Win32; +using Windows.Win32.Foundation; + +namespace DevHomeAzureExtension.Helpers; + +public static class RuntimeHelper +{ + public static bool IsMSIX + { + get + { + uint length = 0; + return PInvoke.GetCurrentPackageFullName(ref length, null) != WIN32_ERROR.APPMODEL_ERROR_NO_PACKAGE; + } + } +} diff --git a/src/AzureExtension/NativeMethods.txt b/src/AzureExtension/NativeMethods.txt index 559bd6d9..4fd89c17 100644 --- a/src/AzureExtension/NativeMethods.txt +++ b/src/AzureExtension/NativeMethods.txt @@ -1,6 +1,7 @@ -CredWrite -CredRead -CredFree -CredDelete -WIN32_ERROR -SHLoadIndirectString \ No newline at end of file +GetCurrentPackageFullName +CredWrite +CredRead +CredFree +CredDelete +WIN32_ERROR +SHLoadIndirectString diff --git a/src/AzureExtension/Providers/DevHomeRepository.cs b/src/AzureExtension/Providers/DevHomeRepository.cs index 89039544..d0ef62e3 100644 --- a/src/AzureExtension/Providers/DevHomeRepository.cs +++ b/src/AzureExtension/Providers/DevHomeRepository.cs @@ -52,4 +52,13 @@ public DevHomeRepository(GitRepository gitRepository) _isPrivate = gitRepository.ProjectReference.Visibility == Microsoft.TeamFoundation.Core.WebApi.ProjectVisibility.Private; _lastUpdated = DateTimeOffset.UtcNow; } + + public DevHomeRepository(DataModel.Repository repository) + { + _name = repository.Name; + _owningAccountName = Path.Join(repository.Clone.Connection.Host, repository.Clone.Organization, repository.Clone.Project); + _cloneUrl = repository.Clone.Uri; + _isPrivate = repository.Private; + _lastUpdated = new(repository.UpdatedAt); + } } diff --git a/src/AzureExtension/Providers/SettingsCardData.cs b/src/AzureExtension/Providers/SettingsCardData.cs new file mode 100644 index 00000000..1286e4b8 --- /dev/null +++ b/src/AzureExtension/Providers/SettingsCardData.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace DevHomeAzureExtension.Providers; + +internal sealed class SettingsCardData +{ + public bool HasOpenAIKey { get; set; } + + public string NotificationsEnabled { get; set; } = string.Empty; + + public string CacheLastUpdated { get; set; } = string.Empty; + + public string UpdateAzureData { get; set; } = string.Empty; +} diff --git a/src/AzureExtension/Providers/SettingsCardSerializerContext.cs b/src/AzureExtension/Providers/SettingsCardSerializerContext.cs new file mode 100644 index 00000000..b847858b --- /dev/null +++ b/src/AzureExtension/Providers/SettingsCardSerializerContext.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Text.Json.Serialization; + +namespace DevHomeAzureExtension.Providers; + +[JsonSourceGenerationOptions(PropertyNameCaseInsensitive = true)] +[JsonSerializable(typeof(SettingsCardData))] +internal sealed partial class SettingsCardSerializerContext : JsonSerializerContext +{ +} diff --git a/src/AzureExtension/Providers/SettingsCardTemplate.json b/src/AzureExtension/Providers/SettingsCardTemplate.json new file mode 100644 index 00000000..7eb75c85 --- /dev/null +++ b/src/AzureExtension/Providers/SettingsCardTemplate.json @@ -0,0 +1,62 @@ +{ + "type": "AdaptiveCard", + "body": [ + { + "type": "Container", + "items": [ + { + "type": "ActionSet", + "actions": [ + { + "type": "Action.Execute", + "title": "%QuickstartPlayground_OpenAI_ClearKey%", + "verb": "ClearOpenAIKey", + "isEnabled": "${HasOpenAIKey}" + } + ] + }, + { + "type": "ActionSet", + "actions": [ + { + "type": "Action.Submit", + "title": "${NotificationsEnabled}", + "verb": "ToggleNotifications", + "associatedInputs": "auto" + } + ] + }, + { + "type": "TextBlock", + "text": "${CacheLastUpdated}", + "size": "medium" + }, + { + "type": "ActionSet", + "actions": [ + { + "type": "Action.Execute", + "title": "${UpdateAzureData}", + "verb": "UpdateData", + "tooltip": "${UpdateAzureData}" + } + ] + }, + { + "type": "ActionSet", + "actions": [ + { + "type": "Action.Execute", + "title": "%Settings_ViewLogs%", + "verb": "OpenLogs", + "tooltip": "%Settings_ViewLogs%" + } + ] + } + ] + } + ], + "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", + "version": "1.5", + "minHeight": "200px" +} diff --git a/src/AzureExtension/Providers/SettingsProvider.cs b/src/AzureExtension/Providers/SettingsProvider.cs index 197285f5..2930b033 100644 --- a/src/AzureExtension/Providers/SettingsProvider.cs +++ b/src/AzureExtension/Providers/SettingsProvider.cs @@ -1,30 +1,31 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using DevHomeAzureExtension.Contracts; -using DevHomeAzureExtension.Helpers; -using Microsoft.Windows.DevHome.SDK; - -namespace DevHomeAzureExtension.Providers; - -public sealed class SettingsProvider : ISettingsProvider -{ - private readonly IAICredentialService _aiCredentialService; - - public string DisplayName => Resources.GetResource(@"SettingsProviderDisplayName"); - - public SettingsProvider(IAICredentialService aiCredentialService) - { - _aiCredentialService = aiCredentialService; - } - - public void Dispose() - { - GC.SuppressFinalize(this); - } - - public AdaptiveCardSessionResult GetSettingsAdaptiveCardSession() - { - return new AdaptiveCardSessionResult(new SettingsUIController(_aiCredentialService)); - } -} +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using DevHomeAzureExtension.Contracts; +using DevHomeAzureExtension.Helpers; +using Microsoft.Windows.DevHome.SDK; +using Serilog; + +namespace DevHomeAzureExtension.Providers; + +public class SettingsProvider(IAICredentialService aiCredentialService) : ISettingsProvider +{ + private static readonly Lazy _logger = new(() => Log.ForContext("SourceContext", nameof(SettingsProvider))); + + private static readonly ILogger _log = _logger.Value; + + string ISettingsProvider.DisplayName => Resources.GetResource(@"SettingsProviderDisplayName", _log); + + private readonly IAICredentialService _aiCredentialService = aiCredentialService; + + public AdaptiveCardSessionResult GetSettingsAdaptiveCardSession() + { + _log.Information($"GetSettingsAdaptiveCardSession"); + return new AdaptiveCardSessionResult(new SettingsUIController(_aiCredentialService)); + } + + public void Dispose() + { + GC.SuppressFinalize(this); + } +} diff --git a/src/AzureExtension/Providers/SettingsUIController.cs b/src/AzureExtension/Providers/SettingsUIController.cs index a3224fb5..21a399ac 100644 --- a/src/AzureExtension/Providers/SettingsUIController.cs +++ b/src/AzureExtension/Providers/SettingsUIController.cs @@ -1,137 +1,174 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System.Text.Json; -using System.Text.Json.Serialization; -using DevHomeAzureExtension.Contracts; -using DevHomeAzureExtension.Helpers; -using DevHomeAzureExtension.QuickStartPlayground; -using Microsoft.Windows.DevHome.SDK; -using Serilog; -using Windows.Foundation; - -namespace DevHomeAzureExtension.Providers; - -internal sealed partial class SettingsUIController : IExtensionAdaptiveCardSession -{ - private static readonly Lazy _logger = new(() => Serilog.Log.ForContext("SourceContext", nameof(SettingsUIController))); - - private static readonly ILogger _log = _logger.Value; - - private readonly IAICredentialService _aiCredentialService; - - private IExtensionAdaptiveCard? _extensionUI; - - public SettingsUIController(IAICredentialService aiCredentialService) - { - _aiCredentialService = aiCredentialService; - _extensionUI = null; - } - - public void Dispose() - { - } - - public ProviderOperationResult Initialize(IExtensionAdaptiveCard extensionUI) - { - _extensionUI = extensionUI; - return UpdateCard(); - } - - private ProviderOperationResult UpdateCard() - { - try - { - var hasOpenAIKey = _aiCredentialService.GetCredentials(OpenAIDevContainerQuickStartProjectProvider.LoginId, OpenAIDevContainerQuickStartProjectProvider.LoginId) is not null; - SettingsAdaptiveCardInputPayload settingsAdaptiveCardInputPayload = new() - { - HasOpenAIKey = hasOpenAIKey, - }; - - return _extensionUI!.Update( - GetTemplate(), - JsonSerializer.Serialize(settingsAdaptiveCardInputPayload, SettingsAdaptiveCardPayloadSourceGeneration.Default.SettingsAdaptiveCardInputPayload), - string.Empty); - } - catch (Exception ex) - { - _log.Error(ex, "Failed to update settings card"); - return new ProviderOperationResult(ProviderOperationStatus.Failure, ex, ex.Message, ex.Message); - } - } - - private static string GetTemplate() - { - var settingsUI = @" -{ - ""type"": ""AdaptiveCard"", - ""body"": [ - { - ""type"": ""ActionSet"", - ""actions"": [ - { - ""type"": ""Action.Execute"", - ""title"": """ + Resources.GetResource(@"QuickstartPlayground_OpenAI_ClearKey") + @""", - ""id"": ""clearOpenAIKey"", - ""isEnabled"": ""${HasOpenAIKey}"" - } - ] - } - ], - ""$schema"": ""http://adaptivecards.io/schemas/adaptive-card.json"", - ""version"": ""1.5"", - ""minHeight"": ""200px"" -} -"; - return settingsUI; - } - - public IAsyncOperation OnAction(string action, string inputs) - { - return Task.Run(() => - { - try - { - var adaptiveCardPayload = JsonSerializer.Deserialize(action, SettingsAdaptiveCardPayloadSourceGeneration.Default.SettingsActionAdaptiveCardActionPayload); - if (adaptiveCardPayload is not null) - { - if (adaptiveCardPayload.IsClearOpenAIKeyAction) - { - _log.Information("Clearing OpenAI key"); - _aiCredentialService.RemoveCredentials(OpenAIDevContainerQuickStartProjectProvider.LoginId, OpenAIDevContainerQuickStartProjectProvider.LoginId); - return UpdateCard(); - } - } - } - catch (Exception ex) - { - _log.Error(ex, "Failed to clear OpenAI key"); - return new ProviderOperationResult(ProviderOperationStatus.Failure, ex, ex.Message, ex.Message); - } - - return new ProviderOperationResult(ProviderOperationStatus.Failure, null, string.Empty, "Unexpected state"); - }).AsAsyncOperation(); - } - - internal sealed class SettingsActionAdaptiveCardActionPayload - { - public string? Id - { - get; set; - } - - public bool IsClearOpenAIKeyAction => Id == "clearOpenAIKey"; - } - - internal sealed class SettingsAdaptiveCardInputPayload - { - public bool HasOpenAIKey { get; set; } - } - - [JsonSourceGenerationOptions(PropertyNameCaseInsensitive = true)] - [JsonSerializable(typeof(SettingsActionAdaptiveCardActionPayload))] - [JsonSerializable(typeof(SettingsAdaptiveCardInputPayload))] - internal sealed partial class SettingsAdaptiveCardPayloadSourceGeneration : JsonSerializerContext - { - } -} +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Globalization; +using System.Text; +using System.Text.Json; +using System.Text.Json.Nodes; +using DevHomeAzureExtension.Contracts; +using DevHomeAzureExtension.DataManager; +using DevHomeAzureExtension.Helpers; +using DevHomeAzureExtension.QuickStartPlayground; +using Microsoft.Windows.DevHome.SDK; +using Serilog; +using Windows.Foundation; + +namespace DevHomeAzureExtension.Providers; + +internal sealed class SettingsUIController(IAICredentialService aiCredentialService) : IExtensionAdaptiveCardSession +{ + private static readonly Lazy _logger = new(() => Serilog.Log.ForContext("SourceContext", nameof(SettingsUIController))); + + private static readonly ILogger _log = _logger.Value; + + private readonly IAICredentialService _aiCredentialService = aiCredentialService; + + private static readonly string _notificationsEnabledString = "NotificationsEnabled"; + + private string? _template; + + private IExtensionAdaptiveCard? _settingsUI; + + public void Dispose() + { + _log.Debug($"Dispose"); + _settingsUI?.Update(null, null, null); + } + + public ProviderOperationResult Initialize(IExtensionAdaptiveCard extensionUI) + { + _log.Debug($"Initialize"); + CacheManager.GetInstance().OnUpdate += HandleCacheUpdate; + _settingsUI = extensionUI; + return UpdateCard(); + } + + private void HandleCacheUpdate(object? source, CacheManagerUpdateEventArgs e) + { + _log.Debug("Cache was updated, updating settings UI."); + UpdateCard(); + } + + public IAsyncOperation OnAction(string action, string inputs) + { + return Task.Run(async () => + { + try + { + _log.Information($"OnAction() called with {action}"); + _log.Debug($"inputs: {inputs}"); + var actionObject = JsonNode.Parse(action); + var verb = actionObject?["verb"]?.GetValue() ?? string.Empty; + _log.Debug($"Verb: {verb}"); + switch (verb) + { + case "ClearOpenAIKey": + _log.Information("Clearing OpenAI key"); + _aiCredentialService.RemoveCredentials(OpenAIDevContainerQuickStartProjectProvider.LoginId, OpenAIDevContainerQuickStartProjectProvider.LoginId); + break; + + case "ToggleNotifications": + var currentNotificationsEnabled = LocalSettings.ReadSettingAsync(_notificationsEnabledString).Result ?? "true"; + await LocalSettings.SaveSettingAsync(_notificationsEnabledString, currentNotificationsEnabled == "true" ? "false" : "true"); + _log.Information($"Changed notification state to: {(currentNotificationsEnabled == "true" ? "false" : "true")}"); + break; + + case "UpdateData": + _log.Information($"Refreshing data for organizations."); + _ = CacheManager.GetInstance().Refresh(); + break; + + case "OpenLogs": + FileHelper.OpenLogsLocation(); + break; + + default: + _log.Warning($"Unknown verb: {verb}"); + break; + } + + return UpdateCard(); + } + catch (Exception ex) + { + _log.Error(ex, "Unexpected failure handling settings action."); + return new ProviderOperationResult(ProviderOperationStatus.Failure, ex, ex.Message, ex.Message); + } + }).AsAsyncOperation(); + } + + private ProviderOperationResult UpdateCard() + { + try + { + var hasOpenAIKey = _aiCredentialService.GetCredentials(OpenAIDevContainerQuickStartProjectProvider.LoginId, OpenAIDevContainerQuickStartProjectProvider.LoginId) is not null; + + var notificationsEnabled = LocalSettings.ReadSettingAsync(_notificationsEnabledString).Result ?? "true"; + var notificationsEnabledString = (notificationsEnabled == "true") ? Resources.GetResource("Settings_NotificationsEnabled", _log) : Resources.GetResource("Settings_NotificationsDisabled", _log); + + var lastUpdated = CacheManager.GetInstance().LastUpdated; + var lastUpdatedString = $"Last updated: {lastUpdated.ToString(CultureInfo.InvariantCulture)}"; + if (lastUpdated == DateTime.MinValue) + { + lastUpdatedString = "Last updated: never"; + } + + var updateAzureDataString = Resources.GetResource("Settings_UpdateData", _log); + if (CacheManager.GetInstance().UpdateInProgress) + { + updateAzureDataString = "Update in progress"; + } + + var settingsCardData = new SettingsCardData + { + HasOpenAIKey = hasOpenAIKey, + NotificationsEnabled = notificationsEnabledString, + CacheLastUpdated = lastUpdatedString, + UpdateAzureData = updateAzureDataString, + }; + + return _settingsUI!.Update( + GetTemplate(), + JsonSerializer.Serialize(settingsCardData, SettingsCardSerializerContext.Default.SettingsCardData), + "SettingsPage"); + } + catch (Exception ex) + { + _log.Error(ex, "Failed to update settings card"); + return new ProviderOperationResult(ProviderOperationStatus.Failure, ex, ex.Message, ex.Message); + } + } + + private string GetTemplate() + { + if (_template is not null) + { + _log.Debug("Using cached template."); + return _template; + } + + try + { + var path = Path.Combine(AppContext.BaseDirectory, @"Providers\SettingsCardTemplate.json"); + var template = File.ReadAllText(path, Encoding.Default) ?? throw new FileNotFoundException(path); + template = Resources.ReplaceIdentifiers(template, GetSettingsCardResourceIdentifiers(), _log); + _log.Debug($"Caching template"); + _template = template; + return _template; + } + catch (Exception e) + { + _log.Error(e, "Error getting template."); + return string.Empty; + } + } + + private static string[] GetSettingsCardResourceIdentifiers() + { + return + [ + "QuickstartPlayground_OpenAI_ClearKey", + "Settings_ViewLogs", + ]; + } +} diff --git a/src/AzureExtension/Strings/en-US/Resources.resw b/src/AzureExtension/Strings/en-US/Resources.resw index 504001cd..6b5c6932 100644 --- a/src/AzureExtension/Strings/en-US/Resources.resw +++ b/src/AzureExtension/Strings/en-US/Resources.resw @@ -1,17 +1,17 @@  - @@ -439,6 +439,21 @@ Dev Boxes aren't configured for the current account. Error shown when Dev Boxes aren't configured for the current account. + + Azure + + + Notifications Disabled + + + Notifications Enabled + + + Update Organization Data + + + View Logs + Choose an image to use Label text for a list of cards that appear in the UI @@ -691,10 +706,6 @@ Clear OpenAI key {Locked="OpenAI"} Text of button to clear OpenAI key. - - Settings - Display name for settings page. - Please check logs at C:\ProgramData\Microsoft\DevBoxAgent\Logs Shown for errors in config flow for Dev Boxes which don't have additional info diff --git a/src/AzureExtension/Widgets/AzurePullRequestsWidget.cs b/src/AzureExtension/Widgets/AzurePullRequestsWidget.cs index 640b7d2d..894f17ac 100644 --- a/src/AzureExtension/Widgets/AzurePullRequestsWidget.cs +++ b/src/AzureExtension/Widgets/AzurePullRequestsWidget.cs @@ -286,8 +286,8 @@ public override void LoadContentData() if (workItem != null) { // If we can't get the real date, it is better better to show a recent - // closer-to-correct time than the zero value decades ago, so use DateTime.Now. - var dateTicks = workItem["CreationDate"]?.GetValue() ?? DateTime.Now.Ticks; + // closer-to-correct time than the zero value decades ago, so use DateTime.UtcNow. + var dateTicks = workItem["CreationDate"]?.GetValue() ?? DateTime.UtcNow.Ticks; var dateTime = dateTicks.ToDateTime(); var item = new JsonObject diff --git a/src/AzureExtension/Widgets/AzureQueryListWidget.cs b/src/AzureExtension/Widgets/AzureQueryListWidget.cs index 811f60ff..28b2f712 100644 --- a/src/AzureExtension/Widgets/AzureQueryListWidget.cs +++ b/src/AzureExtension/Widgets/AzureQueryListWidget.cs @@ -297,8 +297,8 @@ public override void LoadContentData() if (workItem != null) { // If we can't get the real date, it is better better to show a recent - // closer-to-correct time than the zero value decades ago, so use DateTime.Now. - var dateTicks = workItem["System.ChangedDate"]?.GetValue() ?? DateTime.Now.Ticks; + // closer-to-correct time than the zero value decades ago, so use DateTime.UtcNow. + var dateTicks = workItem["System.ChangedDate"]?.GetValue() ?? DateTime.UtcNow.Ticks; var dateTime = dateTicks.ToDateTime(); var item = new JsonObject diff --git a/src/AzureExtension/Widgets/AzureWidget.cs b/src/AzureExtension/Widgets/AzureWidget.cs index 1bdac853..b34c5f2a 100644 --- a/src/AzureExtension/Widgets/AzureWidget.cs +++ b/src/AzureExtension/Widgets/AzureWidget.cs @@ -463,7 +463,7 @@ private async Task PeriodicUpdate() { // Only update per the update interval. // This is intended to be dynamic in the future. - if (DateTime.Now - _lastUpdateRequest < WidgetRefreshRate) + if (DateTime.UtcNow - _lastUpdateRequest < WidgetRefreshRate) { return; } @@ -482,7 +482,7 @@ private async Task PeriodicUpdate() Log.Error(ex, "Failed Requesting Update"); } - _lastUpdateRequest = DateTime.Now; + _lastUpdateRequest = DateTime.UtcNow; } // This method will attempt to select a DeveloperId if one is not already selected. By default diff --git a/src/AzureExtensionServer/Program.cs b/src/AzureExtensionServer/Program.cs index 5774124d..936db8a2 100644 --- a/src/AzureExtensionServer/Program.cs +++ b/src/AzureExtensionServer/Program.cs @@ -156,6 +156,10 @@ private static void HandleCOMServerActivation() var widgetProviderInstance = new Widgets.WidgetProvider(); widgetServer.RegisterWidget(() => widgetProviderInstance); + // Cache manager updates account data. + using var cacheManager = DataManager.CacheManager.GetInstance(); + cacheManager?.Start(); + // This will make the main thread wait until the event is signaled by the extension class. // Since we have single instance of the extension object, we exit as soon as it is disposed. extensionDisposedEvent.WaitOne(); @@ -226,7 +230,7 @@ private static void RecreateDataStoreIfNecessary() private static IHost CreateHost() { - var host = Microsoft.Extensions.Hosting.Host. + var host = Host. CreateDefaultBuilder(). UseContentRoot(AppContext.BaseDirectory). UseDefaultServiceProvider((context, options) => diff --git a/test/AzureExtension/AzureExtension.Test.csproj b/test/AzureExtension/AzureExtension.Test.csproj index 08b9682b..da69cf65 100644 --- a/test/AzureExtension/AzureExtension.Test.csproj +++ b/test/AzureExtension/AzureExtension.Test.csproj @@ -4,7 +4,7 @@ enable enable - AzureExtension.Test + DevHomeAzureExtension.Test win10-x86;win10-x64;win10-arm64 true false diff --git a/test/AzureExtension/DataStore/DataObjectTests.cs b/test/AzureExtension/DataStore/DataObjectTests.cs index 18195d7a..a9081115 100644 --- a/test/AzureExtension/DataStore/DataObjectTests.cs +++ b/test/AzureExtension/DataStore/DataObjectTests.cs @@ -13,7 +13,7 @@ public partial class DataStoreTests [TestCategory("Unit")] public void DateTimeExtension() { - var now = DateTime.Now; + var now = DateTime.UtcNow; TestContext?.WriteLine($"Now: {now}"); var nowAsInteger = now.ToDataStoreInteger(); TestContext?.WriteLine($"NowAsDataStoreInteger: {nowAsInteger}"); @@ -221,6 +221,39 @@ public void ReadAndWriteProject() Assert.AreEqual(2, project2.Id); } + [TestMethod] + [TestCategory("Unit")] + public void ReadAndWriteRepository() + { + using var dataStore = new DataStore("TestStore", TestHelpers.GetDataStoreFilePath(TestOptions), TestOptions.DataStoreOptions.DataStoreSchema!); + Assert.IsNotNull(dataStore); + dataStore.Create(); + Assert.IsNotNull(dataStore.Connection); + + using var tx = dataStore.Connection.BeginTransaction(); + var org = Organization.GetOrCreate(dataStore, new Uri("https://dev.azure.com/organization/")); + Assert.IsNotNull(org); + dataStore.Connection.Insert(new Project { Name = "project", InternalId = "11", OrganizationId = org.Id }); + + dataStore.Connection.Insert(new Repository { Name = "R1", InternalId = "21", CloneUrl = "https://organization/project/_git/repository1/", ProjectId = 1 }); + dataStore.Connection.Insert(new Repository { Name = "R2", InternalId = "22", CloneUrl = "https://organization/project/_git/repository2/", ProjectId = 1 }); + tx.Commit(); + + // Verify retrieval and input into data objects. + var dataStoreRepositories = dataStore.Connection.GetAll().ToList(); + Assert.AreEqual(dataStoreRepositories.Count, 2); + for (var i = 1; i < 3; ++i) + { + var repository = Repository.Get(dataStore, i); + Assert.IsNotNull(repository); + Assert.AreEqual($"R{i}", repository.Name); + Assert.AreEqual($"2{i}", repository.InternalId); + Assert.AreEqual("organization", repository.Project.Organization.Name); + Assert.AreEqual("https://dev.azure.com/organization/", repository.Project.Organization.ConnectionUri.ToString()); + Assert.AreEqual($"https://organization/project/_git/repository{i}/", repository.Clone.Uri.ToString()); + } + } + [TestMethod] [TestCategory("Unit")] public void ReadAndWriteQuery() From b60f920db18eab09bef2a985a79a1e94eb207183 Mon Sep 17 00:00:00 2001 From: adrastogi Date: Tue, 9 Jul 2024 16:07:26 -0700 Subject: [PATCH 08/11] Add mitigation for scenarios where model can get stuck when generating install commands in Dockerfile (#234) Co-authored-by: Aditya Rastogi --- .../.devcontainer/Dockerfile | 3 +- .../rust-linked-list/.devcontainer/Dockerfile | 2 +- .../docsEmbeddings-OpenAI.json | 3078 ++++++++--------- .../QuickStartPlayground/Completions.cs | 3 + 4 files changed, 1544 insertions(+), 1542 deletions(-) diff --git a/src/AzureExtension/Assets/QuickStartPlayground/Samples/csharp-hello-world/.devcontainer/Dockerfile b/src/AzureExtension/Assets/QuickStartPlayground/Samples/csharp-hello-world/.devcontainer/Dockerfile index 2e3b51f4..4e6bb360 100644 --- a/src/AzureExtension/Assets/QuickStartPlayground/Samples/csharp-hello-world/.devcontainer/Dockerfile +++ b/src/AzureExtension/Assets/QuickStartPlayground/Samples/csharp-hello-world/.devcontainer/Dockerfile @@ -3,5 +3,4 @@ FROM mcr.microsoft.com/dotnet/sdk:8.0 ENV DEBIAN_FRONTEND noninteractive -RUN apt-get update && \ -apt-get install -y git \ No newline at end of file +RUN apt-get update && apt-get upgrade -y \ No newline at end of file diff --git a/src/AzureExtension/Assets/QuickStartPlayground/Samples/rust-linked-list/.devcontainer/Dockerfile b/src/AzureExtension/Assets/QuickStartPlayground/Samples/rust-linked-list/.devcontainer/Dockerfile index dcac5489..3fe84173 100644 --- a/src/AzureExtension/Assets/QuickStartPlayground/Samples/rust-linked-list/.devcontainer/Dockerfile +++ b/src/AzureExtension/Assets/QuickStartPlayground/Samples/rust-linked-list/.devcontainer/Dockerfile @@ -3,5 +3,5 @@ FROM mcr.microsoft.com/devcontainers/base:ubuntu ENV DEBIAN_FRONTEND noninteractive RUN apt update && \ -apt install -y curl && build-essentials \ +apt install -y curl && build-essential \ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \ No newline at end of file diff --git a/src/AzureExtension/Assets/QuickStartPlayground/docsEmbeddings-OpenAI.json b/src/AzureExtension/Assets/QuickStartPlayground/docsEmbeddings-OpenAI.json index 2dee7910..fcc965a6 100644 --- a/src/AzureExtension/Assets/QuickStartPlayground/docsEmbeddings-OpenAI.json +++ b/src/AzureExtension/Assets/QuickStartPlayground/docsEmbeddings-OpenAI.json @@ -4639,1546 +4639,1546 @@ }, { "name": "csharp-hello-world", - "codespaces": "\n\n=== ./.devcontainer/devcontainer.json ===\n{\n \"name\": \"C#\",\n \"build\": {\n \"dockerfile\": \"Dockerfile\"\n },\n \"customizations\": {\n // Configure properties specific to VS Code.\n \"vscode\": {\n \"settings\": {},\n \"extensions\": [\n \"ms-dotnettools.csharp\"\n ]\n }\n },\n \"postCreateCommand\": \"dotnet restore\"\n}\n\n=== ./.devcontainer/Dockerfile ===\n# Get the .NET Core preinstalled image from Docker Hub\nFROM mcr.microsoft.com/dotnet/sdk:8.0\n\nENV DEBIAN_FRONTEND noninteractive\n\nRUN apt-get update && \\\napt-get install -y git", + "codespaces": "\n\n=== ./.devcontainer/devcontainer.json ===\n{\n \"name\": \"C#\",\n \"build\": {\n \"dockerfile\": \"Dockerfile\"\n },\n \"customizations\": {\n // Configure properties specific to VS Code.\n \"vscode\": {\n \"settings\": {},\n \"extensions\": [\n \"ms-dotnettools.csharp\"\n ]\n }\n },\n \"postCreateCommand\": \"dotnet restore\"\n}\n\n=== ./.devcontainer/Dockerfile ===\n# Get the .NET Core preinstalled image from Docker Hub\nFROM mcr.microsoft.com/dotnet/sdk:8.0\n\nENV DEBIAN_FRONTEND noninteractive\n\nRUN apt-get update && apt-get upgrade -y", "readme": "\n\n=== ./README.md ===\n# C# Hello World App\n\nThis repository contains a C# hello world app.\n\n## Setup\n\nRun `dotnet restore`\n\n## Running the App\n\nRun `dotnet run`", - "code": "\n\n=== ./HelloWorld.csproj ===\n\n \n Exe\n net8.0\n \n\n\n=== ./Program.cs ===\nusing System;\n\nnamespace HelloWorld\n{\n class Program\n {\n static void Main(string[] args)\n {\n Console.WriteLine(\"Hello World!\");\n Console.Write(\"Input: \");\n string data = Console.ReadLine();\n Console.WriteLine(\"Output: \" + data);\n }\n }\n}\n", + "code": "\n\n=== ./HelloWorld.csproj ===\n\n \n Exe\n net8.0\n \n\n\n=== ./Program.cs ===\nnamespace HelloWorld;\n\npublic class Program\n{\n public static void Main(string[] args)\n {\n Console.WriteLine(\"Hello World!\");\n Console.Write(\"Input: \");\n var data = Console.ReadLine();\n Console.WriteLine(\"Output: \" + data);\n }\n}\n", "embedding": [ - -0.027016763, - -0.014102959, - 0.06567473, - 0.0087309, - 0.017806029, - -0.04940208, - -0.002081021, - 0.003893439, - -0.010806705, - 0.038678825, - -0.0009857467, - -0.006623801, - -0.028518854, - 0.037030697, - -0.0018397998, - 0.008240635, - -0.035507746, - 0.01314329, - 0.015552893, - 0.0132058775, - 0.0010665884, - 0.0071297134, - -0.054033525, - 0.027872121, - -0.006498627, - -0.03068854, - -0.0016064021, - -0.024471555, - -0.032503564, - -0.011828961, - 0.03924211, - -0.013445795, - -0.015928416, - 0.008699606, - 0.046773423, - 0.025472948, - 0.05098762, - 0.0017928595, - 0.013706574, - 0.019182945, - 0.00038073817, - -0.004918303, - 0.036655176, - 0.018004222, - -0.0034527215, - 0.004568858, - -0.016022297, - 0.005528527, - 0.01763913, - 0.033212885, - -0.016011866, - -0.026390892, - 0.024096033, - 0.012423539, - 0.0072966125, - -0.04082765, - 0.010984035, - 0.010436398, - 0.0022687824, - 0.06751061, - 0.0151043525, - -0.028414542, - 0.0016572542, - -0.0031111003, - -0.009069913, - 0.006509058, - 0.025514673, - 0.0232824, - 0.04172473, - 0.026077956, - -0.020184338, - -0.013341483, - -0.012141896, - 0.012287933, - -0.034443766, - -0.0029155156, - -0.007849465, - -0.020768484, - 0.0425175, - -0.014582793, - -0.012027154, - -0.026662104, - 0.010076523, - -0.054701123, - -0.005669348, - 0.01429072, - -0.031335272, - -0.0030302585, - -0.0066968193, - -0.015646774, - 0.01728447, - 0.029311623, - 0.0013312796, - -0.015041766, - 0.07911009, - 0.033400647, - -0.037281048, - 0.028456267, - 0.033296335, - 0.005079986, - 0.024534142, - 0.018265001, - -0.016898517, - -0.0027329698, - 0.04681515, - -0.0033249394, - -0.0043080784, - -0.01102576, - -0.010639806, - 0.053407654, - -0.08457603, - -0.008444042, - 0.040848512, - -0.066634394, - 0.020246925, - 0.019412432, - 0.054283876, - -0.050862446, - 0.01429072, - 0.020612016, - 0.024388105, - 0.033817895, - -0.021070989, - -0.02901955, - -0.029937495, - 0.0033640563, - 0.022614805, - 0.037281048, - 0.00227139, - 0.048358962, - 0.045187883, - 0.0231155, - 0.01597014, - 0.016585581, - -0.022802565, - -0.010545926, - -0.0019519351, - -0.00502783, - 0.004274177, - 0.04226715, - 0.035966717, - -0.052447986, - -0.011192659, - 0.0037187166, - 0.03289995, - -0.06776096, - -0.025264325, - 0.0037760881, - -0.015438151, - 0.017002828, - 0.037197597, - 0.064965405, - -0.052072465, - 0.006279572, - 0.033296335, - -0.02937421, - 0.009403711, - 0.007969423, - -0.011662062, - -0.041849904, - -0.0068950118, - 0.0104833385, - -0.054617673, - 0.0148122795, - -0.033859618, - -0.0460641, - 0.0032962537, - 0.030897163, - -0.003358841, - 0.006816778, - 0.0043993513, - -0.014749693, - -0.01014954, - 0.0015333839, - 0.023866545, - 0.015824104, - 0.09137716, - -0.0814884, - -0.014436757, - -0.0015907554, - 0.002782518, - 0.0057840906, - 0.035132222, - -0.028998688, - 0.0022922524, - 0.00043028628, - 0.053491104, - -0.025598122, - -0.017952066, - 0.052781783, - -0.047566194, - 0.029603697, - -0.007009755, - 0.00025246723, - -0.015010472, - -0.012298364, - 0.057872202, - -0.02997922, - -0.004756619, - -0.0040133977, - 0.0039508105, - 0.0006160917, - -0.042580087, - -0.04568858, - 0.024867939, - -0.017180158, - 0.02063288, - 0.025347775, - -0.026745552, - 0.023136362, - 0.022176694, - -0.025347775, - -0.05616149, - -0.012559144, - -0.063129514, - 0.021634273, - 0.019109927, - 0.0022257536, - -0.007093204, - -0.030980613, - -0.031606484, - 0.06333814, - 0.016168334, - -0.058164276, - 0.036905523, - -0.009330693, - 0.033171162, - 0.011536888, - 0.045479957, - -0.018588368, - 0.0054398617, - 0.0077608, - 0.013414501, - -0.034151692, - 0.0069680302, - -0.020486843, - 0.019986145, - -0.014029941, - -0.027329698, - 0.0065142736, - -0.040472988, - -0.031189237, - -0.0118915485, - -0.018379744, - 0.036822073, - 0.0066446634, - -0.004250707, - -0.067135096, - 0.01594928, - -0.0036274437, - -0.005424215, - 0.03746881, - -0.009221165, - 0.039116934, - 0.00010691962, - -0.021237887, - 0.012986822, - -0.000548615, - 0.04831724, - -0.03951332, - 0.012413108, - -0.06788614, - -0.008193694, - 0.0004312642, - -0.024659315, - -0.004503663, - -0.0032049809, - 0.017388782, - -0.017420076, - 0.011693356, - 0.018004222, - -0.0024800138, - 0.013737868, - -0.0068063466, - 0.0019714935, - -0.02010089, - -0.048817936, - 0.017691286, - 0.00023519057, - 0.036926385, - -0.06972203, - 0.0012230562, - 0.021529961, - 0.019902697, - -0.021759447, - 0.0017354881, - 0.030333878, - 0.0018697896, - -0.027496597, - -0.03237839, - 0.0051164953, - 0.015532031, - -0.021123145, - 0.0074739424, - 0.07201689, - 0.039909706, - 0.0041959435, - -0.038866587, - 0.035069637, - -0.012235777, - -0.029228173, - 0.014791417, - 0.016387388, - 0.010107816, - 0.005617192, - 0.0044045667, - 0.039826255, - -0.060709484, - 0.033317197, - -0.026995901, - -0.007108851, - 0.017597405, - 0.019558467, - 0.0071036355, - -0.040431265, - -0.0051217107, - 0.030834576, - -0.008934308, - 0.023678785, - 0.024033446, - 0.015751086, - -0.037531395, - -0.0013495343, - 6.5276385e-05, - -0.009711431, - -0.034527216, - 0.039784532, - 0.002362663, - -1.3752047e-07, - 0.033817895, - -0.0027121075, - -0.022322731, - -0.024867939, - 0.018452762, - 0.03774002, - -0.007108851, - -0.025243463, - 0.016324801, - -0.013435364, - -0.0011963262, - -0.046982046, - -0.022906877, - -0.020236494, - -0.021571686, - 0.007917267, - -0.020758053, - 0.0018306726, - 0.014395032, - 0.044728912, - 0.021404786, - -0.049068283, - -0.026474342, - -0.00012036607, - -0.01375873, - -0.012267071, - -0.029228173, - -0.0232824, - -0.038345028, - -0.023011189, - 0.002015826, - -0.031648207, - 0.0014212487, - 0.016815066, - -0.07548004, - 0.04030609, - 0.018974321, - -0.026849864, - -0.060792934, - -0.022927739, - 0.036383964, - 0.022114107, - 0.0425175, - 0.029103, - 0.016794205, - -0.045271333, - -0.03851193, - 0.04385269, - -0.0097531555, - 0.05052865, - -0.0203721, - 0.022781704, - 0.009242028, - -0.00903862, - -0.0232824, - 0.014780986, - 0.017962497, - 0.026995901, - 0.038324166, - -0.004647092, - 0.036300518, - 0.059624642, - -0.018650955, - 0.02743401, - -0.00965406, - 0.019610623, - 0.0068063466, - -0.004375881, - -0.026349168, - 0.052781783, - -0.029854044, - 0.02442983, - 0.0037082855, - 0.052948684, - 0.013247602, - -0.012329658, - -0.04418649, - -0.0053172954, - 0.017128002, - 0.045813754, - -0.016783774, - 0.015646774, - -0.017221883, - 0.059833266, - 0.015771948, - -0.018244138, - -0.021884622, - -0.03095975, - -0.032086316, - -0.024262931, - 0.016157903, - -0.015573756, - -0.014040372, - -0.0331503, - -0.03563292, - 0.01385261, - 0.0063473745, - 0.014603656, - -0.007004539, - -0.013967354, - -0.023824822, - 0.003012004, - -0.016032727, - 0.031543896, - 0.025556399, - 0.04331027, - -0.052155912, - -0.0022114108, - 0.010478123, - 0.036738627, - 0.052239362, - -0.010827567, - -0.012340089, - -0.018661385, - -0.016543856, - -0.030646814, - -0.030020945, - -0.031877693, - -0.06358849, - -0.044562012, - 0.038073815, - -0.032753915, - -0.058456346, - 0.025806746, - 0.02478449, - -0.023011189, - -0.036905523, - 0.0127677675, - -0.020309513, - -0.015938846, - 0.0075000203, - 0.021884622, - 0.025744159, - -0.057788752, - 0.042621814, - 0.026808139, - 0.045354784, - 0.022656528, - -0.02115444, - 0.035153087, - -0.06413091, - 0.013362345, - 0.008856074, - 0.030938888, - -0.034986187, - -0.019162083, - -0.008866506, - 0.037427083, - 0.03068854, - 0.0042924318, - 0.02628658, - 0.017858185, - -0.019871403, - 0.022364456, - -0.014415895, - 0.01755568, - 0.11215608, - -0.04552168, - -0.030751126, - -0.0064047463, - -0.01754525, - -0.027559185, - 0.031585623, - 0.0350905, - -0.0071557914, - -0.008105029, - -0.040514715, - 0.027580047, - -0.031189237, - -0.021529961, - -0.011536888, - -0.01835888, - -0.01957933, - 0.0153964255, - 0.0580391, - 0.06842856, - -0.040076602, - -0.006931521, - 0.010660669, - -0.012402676, - -0.06692647, - 0.044854086, - 0.014050803, - 0.031147512, - 0.014009078, - -0.022176694, - 0.035466023, - -0.047941715, - 0.041683007, - 0.016251782, - -0.03519481, - 0.014207271, - 0.03732277, - -0.03688466, - -0.0060083615, - -0.0072653187, - -0.02503484, - -0.04831724, - 0.019068202, - 0.010973604, - -0.01870311, - -0.0009909624, - 0.037614845, - 0.029207312, - -0.042475775, - -0.025201738, - -0.01816069, - -0.036988974, - 0.0034422902, - 0.0050982405, - -0.041683007, - -0.012997254, - -0.013904766, - 0.059249118, - -0.0027173231, - 0.0023444083, - -0.016011866, - 0.015719792, - 0.020570293, - 0.034318592, - 0.0020066989, - 0.008605726, - -0.0055180956, - -0.004401959, - 8.874655e-05, - 0.04806689, - -0.004214198, - -0.012986822, - -0.010034798, - -0.0030328664, - 0.039805394, - 0.024367243, - 0.010858861, - 0.052948684, - 0.0024096032, - -0.017399212, - 0.022009796, - 0.005669348, - -0.026557792, - -0.017920772, - -0.029770596, - -0.017274039, - -0.0013834356, - -0.037781745, - -0.024262931, - -0.013727437, - 0.006769838, - 0.005113887, - 0.0027042842, - 0.014155115, - 0.015375563, - 0.00047820454, - -0.002399172, - -0.026411755, - 0.031418722, - -0.039325558, - -0.012663456, - 0.050194852, - -0.0031111003, - 0.0271628, - -0.023073776, - 0.020267788, - -0.0134040695, - -0.0015203449, - 0.033546682, - 0.009560179, - 0.015354701, - -0.0068063466, - -0.010379027, - -0.020622449, - 0.0121836215, - 0.010290362, - 0.023678785, - -0.012736474, - -0.03577896, - 0.031439584, - -0.014885298, - -0.005538958, - -0.0007595205, - 0.015991002, - -0.02089366, - -0.014061234, - 0.060751207, - -0.003074591, - -0.042225428, - -0.0004918955, - 0.0113074025, - 0.048275515, - -0.0015138254, - -0.027726084, - -0.0036743842, - -0.021363063, - -0.026077956, - -0.0027903414, - 0.050153125, - 0.026536928, - -0.014687105, - -0.018035514, - 0.019475019, - -0.012235777, - 0.022635667, - 0.006039655, - 0.027538322, - -0.015208664, - -0.01385261, - 0.00068454636, - -0.014353307, - -0.009335908, - -0.0015346877, - -0.010535494, - -0.042976473, - -0.0032284511, - 0.013122428, - -0.0061909067, - -0.0059666364, - 0.032420117, - 0.060375687, - 0.021310907, - 0.012340089, - 0.017879046, - -0.01304941, - -0.014864435, - 0.021926345, - -0.024304656, - 0.0023196342, - 0.059040494, - -0.01648127, - -0.04648135, - -0.018995184, - -0.027308837, - 0.021571686, - -0.013174584, - 0.019370707, - -0.018859578, - -0.011088347, - 0.0071923006, - 0.040264364, - -0.028706614, - 0.036279652, - -0.033817895, - -0.026161406, - -0.042621814, - -0.04134921, - 0.02072676, - -0.007093204, - 0.012194052, - 0.0156885, - 0.022719117, - 0.00046483957, - 0.0310432, - -0.026495203, - -0.0056589167, - 0.011057054, - -0.045187883, - -0.013237171, - -0.0044045667, - 0.018014653, - -0.013769161, - -0.02178031, - -0.015844967, - -0.028080745, - -0.008371024, - -0.039096072, - -0.03244098, - 0.025618985, - 0.013800454, - -0.015125215, - 0.015980572, - 0.017659992, - 0.012757337, - -0.032607876, - 0.021655135, - -0.017618267, - -0.0013260641, - -0.0046131904, - 0.0061335354, - -0.012506988, - -0.033296335, - -0.029520247, - 0.03915866, - 0.003867361, - 0.014572362, - -0.0014955709, - -0.04260095, - -0.032169767, - -0.041140586, - -0.0073174746, - 0.004787913, - -0.01049377, - 0.0013247603, - -0.015511169, - -0.0032962537, - 0.013612693, - -0.015239958, - -0.048525862, - 0.015991002, - -0.012121034, - 0.029624559, - 0.0032884304, - 0.060125336, - 0.0034527215, - -0.0005407916, - -0.019808816, - -0.009137716, - 0.029082138, - -0.013362345, - -0.0024956604, - -0.0428513, - -0.024575867, - 0.021467375, - -0.0060813795, - 0.02116487, - 0.027892983, - 0.02063288, - 0.029520247, - 0.003523132, - 0.031439584, - 0.0045349565, - 0.009153363, - -0.02470104, - 0.00012558166, - 0.012162759, - 0.0024161227, - -0.023198951, - 0.008480552, - 0.0075886855, - -0.00055187475, - -0.016992396, - 0.01278863, - 0.016825497, - 0.023261538, - -0.013466657, - 0.025869332, - 0.02637003, - 0.018567504, - -0.031877693, - -0.026495203, - 0.05319903, - -0.013070272, - 0.009930486, - 0.03043819, - -0.00072431524, - -0.03448549, - -0.0029076922, - -0.047858268, - 0.048192065, - -0.043185096, - -0.046898596, - -0.0039899275, - -0.008668313, - 0.024283793, - 0.0006470593, - 0.01251742, - -0.015490307, - 0.005254708, - 0.029395074, - -0.038303304, - 0.021842897, - 0.040014017, - 0.0055076643, - -0.04577203, - 0.008277143, - -0.010681531, - -0.026933314, - 0.027287973, - -0.021509098, - 0.027642634, - -0.019214239, - -0.019537605, - -0.034986187, - -0.011203091, - -0.008355377, - -0.02776781, - 0.0077034286, - 0.0049365573, - -0.06684302, - -0.014561931, - 0.024179481, - -0.014165546, - -0.032879088, - -0.024262931, - -0.0056328387, - 0.032399252, - 0.0018776129, - 0.009930486, - -0.048651036, - -0.000702149, - 0.02221842, - -0.0055337423, - -0.025535535, - 0.018244138, - 0.020382531, - 0.05336593, - -0.008099813, - -0.02822678, - -0.005361628, - 0.012225346, - -0.032607876, - -0.008829996, - -0.0028112037, - -0.028977826, - -0.04072334, - -0.022656528, - -0.02230187, - -0.012840786, - 0.028164193, - 0.020455549, - 0.028560579, - -0.02593192, - -0.034631528, - 0.013164152, - 0.0038908313, - -0.018598799, - 0.0024082994, - -0.0020458158, - 0.029624559, - -0.013237171, - -0.0007464815, - -0.008183263, - -0.039325558, - 0.027037626, - 0.0073748464, - -0.004441076, - 0.032774776, - -0.02115444, - 0.011745512, - 0.023219813, - 0.012506988, - -0.0126217315, - -0.028122468, - -0.030417329, - 0.035048775, - 0.008271928, - -0.007823387, - -0.026307443, - 0.046940323, - -0.037093285, - -0.010024367, - 0.007546961, - 0.060167063, - 0.051446594, - 0.0014851396, - -0.0032075888, - -0.005306864, - 0.016387388, - 0.03252443, - 0.05970809, - -0.029332485, - -0.03573723, - -0.014739261, - -0.00434198, - 0.036968112, - -0.010785843, - 0.014603656, - -0.004135964, - -0.0019558468, - -0.010681531, - -0.000855357, - 0.031064061, - 0.018796992, - 0.020330375, - 0.015323407, - -0.016429113, - 0.016721185, - -0.033546682, - 0.024888802, - -0.028602304, - -0.0027642634, - 0.00153208, - -0.007990286, - 0.011839393, - 0.021467375, - 0.014457619, - 0.029582834, - 0.025618985, - -0.0040994547, - -0.0017068023, - -0.012924235, - 0.01084843, - -0.015980572, - -0.020747622, - -0.0460641, - -0.037051562, - 0.01676291, - 0.018901303, - 0.04072334, - -0.015938846, - 0.0389709, - 0.0058884025, - 0.0067072506, - 0.020862365, - -0.025723297, - -0.04020178, - -0.028623166, - 0.016898517, - 0.013915198, - -0.0057580126, - -0.04452029, - 0.05211419, - 0.04347717, - -0.004905264, - 0.00763041, - 0.042225428, - -0.010253852, - -0.023407575, - 0.025702434, - -0.019370707, - -0.044061314, - 0.01534427, - -0.035591196, - 0.0019245532, - -0.025514673, - 0.042475775, - 0.0074843736, - -0.006555998, - 0.03554947, - 4.8325714e-05, - -0.017347056, - 0.013925629, - -0.008710038, - 0.01736792, - -0.054200426, - 0.0005759969, - 0.011735081, - -0.011766374, - -0.016470838, - 0.015239958, - 0.026307443, - -0.032336667, - -0.03189856, - -0.014916591, - 0.0009779234, - -0.015782379, - 0.018890873, - -0.010785843, - -0.0007927699, - -0.019036908, - 9.339168e-05, - -0.018943029, - 0.011265677, - -0.020935385, - 0.018901303, - -0.020403393, - -0.0011715522, - 0.01788948, - -0.037552256, - 0.0006434736, - 0.00518169, - 0.020956246, - 0.04118231, - 0.017347056, - 0.008449258, - 0.014572362, - 0.0047540115, - -0.0004899396, - -0.0038099894, - -0.021029264, - -0.0034370746, - 0.026495203, - -0.017347056, - -0.0032623524, - -0.048192065, - -0.0068115625, - -0.022948602, - -0.0027512244, - 0.004464546, - -0.006634232, - -0.050945897, - 0.007541745, - -0.0021253536, - 0.0028398896, - 0.0006969334, - 0.0017811245, - 0.003929948, - -0.006180476, - 0.0271628, - 0.026328305, - -0.020705897, - 0.0038725766, - 0.030187843, - -0.0072601032, - 0.039450735, - -0.013164152, - -0.006263925, - 0.0075886855, - -0.0425175, - 0.016105747, - -0.04648135, - 0.005679779, - 0.013675281, - -0.018974321, - -0.051989015, - -0.032607876, - 0.0078807585, - -0.03774002, - 0.025723297, - 0.02029908, - 0.026703827, - -0.004073377, - -0.0061178887, - 0.05219764, - -0.041766457, - 0.026787277, - -0.023887409, - 0.0018932597, - 0.0012908588, - 0.019026477, - -0.0034396825, - -0.011223952, - -0.0195689, - -0.01694024, - -0.02415862, - -0.002015826, - 0.018796992, - 0.058706697, - -0.015907554, - 0.028894376, - -0.0038960467, - -0.006623801, - 0.0024982681, - 0.0016324801, - -0.026557792, - 0.039221246, - 0.0054346463, - 0.00045473437, - 0.033129435, - -0.015312976, - 0.013153722, - -0.003139786, - 0.018557074, - 0.032879088, - -0.029874908, - -0.019203808, - -0.03986798, - -0.016095314, - -0.019109927, - -0.044937536, - 0.021300474, - 0.005184298, - 0.021217026, - 0.047691368, - -0.0020992756, - 0.05178039, - 0.016856791, - 0.024388105, - -0.04013919, - -0.013289327, - -0.012799061, - 0.013153722, - -0.0027512244, - -0.018723972, - 0.002539993, - 0.02081021, - 0.018525781, - -0.043518893, - -0.0009049051, - -0.028205918, - 0.010712825, - -0.026161406, - 0.012684318, - -0.03776088, - -0.019297687, - -0.00055448257, - -0.014801849, - 0.00084818556, - -0.025118288, - 0.028956963, - 0.009867899, - 0.018890873, - 0.030125255, - -0.023157226, - 0.008809133, - 0.02284429, - 0.03440204, - -0.010691962, - -0.0015777164, - -0.008459689, - 0.010942311, - -0.051738665, - -0.022593942, - 0.0045271334, - 0.030125255, - 0.00041789925, - 0.02196807, - -0.03730191, - 0.027892983, - -0.024200344, - -0.023303263, - 0.020163476, - -0.05616149, - 0.028435403, - -0.0009629286, - -0.03308771, - 0.004060338, - 0.031815108, - -0.009226381, - -0.00084427383, - 0.009930486, - 0.035862405, - -0.022134969, - 0.017138433, - -0.008324084, - -0.012058447, - 0.027350562, - -0.04664825, - 0.00403426, - 0.03367186, - 0.017785167, - -0.027371423, - -0.033358924, - 0.0024343773, - 0.015563325, - 0.007093204, - 0.022948602, - 0.014009078, - -0.054200426, - 0.0023026837, - -0.03306685, - 0.029395074, - 0.008235419, - -0.038720552, - 0.003048513, - 0.022927739, - -0.019266395, - -0.019808816, - -0.00066303206, - -0.02970801, - 0.010900586, - 0.0041594342, - 0.0006333684, - -0.021227457, - 0.000140332, - -0.0065142736, - 0.034861013, - 0.027851257, - -0.032649603, - -0.03360927, - -0.022510493, - 0.001558158, - -0.023887409, - 0.0019858363, - -0.034714974, - 0.0017420076, - -0.019214239, - -0.027392285, - 0.022823427, - -0.0124756945, - -0.003979496, - 0.011046623, - -0.015208664, - 0.024408968, - -0.025201738, - -0.01694024, - 0.012016723, - -0.010806705, - -0.048525862, - -0.0145202065, - 0.003142394, - -0.012590437, - 0.018150259, - 0.002284429, - -0.0018045946, - -0.033275474, - 0.0015033942, - 0.0024930527, - 0.006342159, - 0.013821317, - -0.008230203, - -0.04401959, - 0.017159296, - -0.017931202, - 0.0072340253, - -0.028456267, - 0.005100848, - 0.013216308, - 0.021675998, - 0.053574555, - 0.008011148, - -0.014854005, - 0.017274039, - -0.01288251, - 0.03757312, - -0.0030172197, - 0.005259924, - -0.033296335, - 0.021738585, - -0.033275474, - 0.06004189, - 0.0034370746, - 0.010723256, - 0.011776805, - -0.0029259468, - 0.025806746, - 0.011808099, - 0.002169686, - -0.003802166, - 0.009841821, - 0.011943704, - -0.008407533, - -0.02576502, - 0.0054033524, - 0.03607103, - 0.022552216, - 0.009080345, - 0.019975714, - 0.0049104793, - -0.023970857, - -0.013821317, - -0.0120897405, - 0.006597723, - -0.0007230113, - -0.017649561, - 0.008772625, - 0.0250557, - -0.03306685, - 0.04443684, - 0.011349127, - -0.00021905484, - 0.0123818135, - 0.002631266, - 0.05132142, - -0.00022198861, - -0.042621814, - 0.017096708, - 0.02407517, - -0.011860255, - -0.029144725, - 0.011192659, - 0.010066091, - -0.017440937, - 0.02451328, - -0.02724625, - 0.045187883, - -0.0501114, - -0.014833142, - -0.04815034, - 0.00518169, - -0.010702393, - 0.022719117, - 0.021529961, - 0.02223928, - -0.014895729, - -0.011881117, - 0.009304615, - 0.030479915, - 0.011870686, - 0.03953418, - 0.011860255, - -0.0068897964, - -0.01605359, - -0.015928416, - 0.021550823, - 0.012068879, - -0.003267568, - -0.008230203, - 0.016011866, - 0.00992527, - 0.021018833, - 0.025785884, - -0.0047435802, - -0.002526954, - 0.021363063, - 0.013800454, - -0.005450293, - 0.024847077, - 0.013664849, - -0.02451328, - -0.049902778, - -0.015125215, - -0.009768803, - 0.009210735, - -0.020309513, - 0.008605726, - -0.016418682, - -0.0037604414, - -0.037155874, - -0.002487837, - 0.00010797904, - -0.013059841, - -0.010942311, - 0.009205518, - -0.012559144, - -0.0044176057, - -0.013247602, - -0.009852252, - -0.023699647, - -0.0056484854, - -0.031543896, - -0.0024200345, - -0.0024956604, - 0.0040812003, - 0.022969464, - -0.013956922, - -0.03473584, - -0.010144325, - 0.025285186, - -0.012340089, - -0.012924235, - -0.022447905, - -0.019892266, - -0.005246885, - 0.010029582, - 0.0039273403, - -0.004840069, - -0.0015972749, - 0.0041907276, - 0.02601537, - -0.004800952, - 0.011609906, - 0.015354701, - -0.028518854, - -0.058539797, - -0.03369272, - 0.018379744, - -0.0030224351, - -0.036822073, - -0.025994508, - -0.0063995305, - -0.016178764, - -0.017044552, - 0.028372817, - -0.01587626, - 0.010024367, - -0.0055754674, - -0.007682566, - 0.012465264, - 0.006446471, - -0.0008710038, - 0.013904766, - 0.041390933, - -0.017190589, - 0.020080026, - 0.0583729, - 0.020434687, - -0.009867899, - 0.0030693754, - -0.0026208346, - -0.0017054983, - 0.0048844013, - 0.035340846, - 0.007463511, - -0.003382311, - -0.024325518, - 0.027976433, - -0.0023274575, - -0.023887409, - 0.0040994547, - 0.024534142, - 0.0007438737, - 0.0054398617, - 0.025326911, - 0.050653823, - -0.013101566, - 0.001901083, - -0.023344986, - -0.0018997792, - -0.0066029388, - -0.02689159, - 0.0015516385, - 0.0021644705, - 0.0056589167, - -0.0057006413, - -0.029854044, - 0.0075260983, - 0.016867222, - -0.024012582, - -0.0010757158, - -0.01816069, - -0.00053394615, - 0.023574473, - 0.014029941, - 0.021801172, - -0.02415862, - -0.010613728, - 0.010702393, - -0.02029908, - 0.03402652, - 0.011317833, - -0.013080703, - 0.01869268, - 0.002553032, - -0.02169686, - 0.021363063, - -0.01614747, - 0.022865152, - 0.0011083132, - -0.029916633, - 0.042475775, - -0.0038308518, - 0.015803242, - -0.0066707414, - -0.020069595, - -0.017274039, - -0.016283076, - 0.03588327, - 0.005883187, - 0.0028268506, - -0.007875543, - -0.023303263, - 0.014436757, - -0.0013469264, - 0.021300474, - 0.030938888, - 0.0090229735, - 0.017002828, - -0.014248996, - -0.0091690095, - -0.022969464, - 0.016001435, - 0.00030560105, - 0.006608154, - -0.006394315, - 0.01181853, - -0.002030169, - 0.0027720868, - 0.045730304, - 0.01472883, - -0.0028112037, - -0.03577896, - 0.0077034286, - 0.0053146877, - -0.013717005, - 0.009810528, - -0.0007060607, - -0.013727437, - -0.004490624, - -0.018974321, - 0.027934708, - 0.007901621, - -0.018369313, - 0.036676038, - 0.026578654, - -0.0006160917, - 0.032566153, - -0.0007503932, - -0.0038699687, - -0.015500737, - 0.01861966, - -0.020601586, - 0.004480193, - 0.039471596, - 0.043185096, - -0.04506271, - 0.0061596134, - -0.007364415, - 5.0240815e-05, - 0.018796992, - 0.0045375647, - -0.0027512244, - 0.011964567, - 0.05282351, - -0.036467414, - 0.02795557, - 0.002627354, - -0.0087830555, - 0.016815066, - 0.01110921, - 0.020048734, - 0.014843573, - 0.021237887, - -0.050194852, - -0.0024826215, - 0.024262931, - -0.009080345, - 0.03360927, - 0.018598799, - -0.0021957639, - -0.014676674, - -0.009294184, - 0.011088347, - 0.0026938529, - 0.045813754, - -0.014634949, - -0.0040368675, - 0.023261538, - -0.016679462, - -0.019151652, - -0.04226715, - 0.029582834, - -0.0019128182, - -0.038407616, - 0.0041802963, - -0.032065455, - 0.039993156, - -0.008626588, - -0.00674376, - -0.0009909624, - 0.0057319347, - -0.027642634, - 0.033233747, - 0.02689159, - -0.02866489, - -0.039763667, - -0.012580006, - -0.03517395, - 0.0014694929, - -0.017263608, - 0.015177371, - 0.016533425, - 0.0036300516, - 0.06162743, - 0.011046623, - -0.01657515, - -0.027475735, - -0.037260186, - -0.014457619, - -0.01217319 + -0.027098652, + -0.014112577, + 0.065670945, + 0.008751258, + 0.017805003, + -0.04948268, + -0.0021069776, + 0.003911468, + -0.010816514, + 0.038634874, + -0.0010580522, + -0.0065712663, + -0.02851721, + 0.037007704, + -0.0018279594, + 0.008203653, + -0.0355057, + 0.013173825, + 0.015562428, + 0.013194686, + 0.0010254566, + 0.0071449485, + -0.054072134, + 0.027828792, + -0.0064826068, + -0.030707633, + -0.0015997905, + -0.024491007, + -0.032522555, + -0.011776127, + 0.03923985, + -0.01344502, + -0.015917068, + 0.008699105, + 0.04677073, + 0.02547148, + 0.05094296, + 0.0018553397, + 0.013737076, + 0.019192271, + 0.00037419712, + -0.004910196, + 0.03661134, + 0.017982323, + -0.0034394844, + 0.0045320876, + -0.015979651, + 0.0055282083, + 0.017669406, + 0.03321097, + -0.016010944, + -0.026389372, + 0.024115505, + 0.012474976, + 0.0072909766, + -0.040783573, + 0.01095211, + 0.010404505, + 0.0022438788, + 0.06750672, + 0.015134774, + -0.028392043, + 0.0016532472, + -0.0030926676, + -0.009053745, + 0.006508683, + 0.025513204, + 0.023281058, + 0.041722327, + 0.026034731, + -0.020204037, + -0.013413728, + -0.012130766, + 0.012308086, + -0.03442092, + -0.0029466394, + -0.007859443, + -0.020767288, + 0.04251505, + -0.014613245, + -0.012026461, + -0.026681429, + 0.010075942, + -0.05465625, + -0.0056585907, + 0.014258605, + -0.03137519, + -0.0030457298, + -0.006670357, + -0.015635442, + 0.017314766, + 0.029289074, + 0.0013403298, + -0.015009607, + 0.07902209, + 0.03339872, + -0.03723718, + 0.028454628, + 0.03331528, + 0.005079693, + 0.024511866, + 0.01830567, + -0.016887112, + -0.0027588888, + 0.046812452, + -0.0032882409, + -0.0043365145, + -0.011004264, + -0.010633978, + 0.053404577, + -0.08452944, + -0.00841748, + 0.040846158, + -0.06663056, + 0.020224897, + 0.019432174, + 0.05432247, + -0.05085952, + 0.014248175, + 0.020579537, + 0.0243867, + 0.033836808, + -0.021038484, + -0.029017879, + -0.029956631, + 0.0033456092, + 0.02257178, + 0.03732062, + 0.002280386, + 0.048356175, + 0.045227002, + 0.02313503, + 0.01595879, + 0.016542902, + -0.02278039, + -0.010550533, + -0.0019231385, + -0.005037971, + 0.0042843614, + 0.04228558, + 0.035943784, + -0.052444965, + -0.011181584, + 0.003705464, + 0.032877192, + -0.06775706, + -0.02526287, + 0.0037915164, + -0.01540597, + 0.017001849, + 0.037174594, + 0.064961664, + -0.052069463, + 0.0062479186, + 0.03331528, + -0.029372519, + 0.0094136, + 0.00796375, + -0.011630098, + -0.041847493, + -0.0068737534, + 0.010493165, + -0.054614525, + 0.014853149, + -0.03385767, + -0.046103172, + 0.0032386957, + 0.030895384, + -0.0033586472, + 0.0067850933, + 0.0044095283, + -0.014759273, + -0.010154171, + 0.001507219, + 0.02386517, + 0.015823193, + 0.091288455, + -0.08148371, + -0.014425495, + -0.0015867522, + 0.0028188648, + 0.005788973, + 0.0351302, + -0.028976155, + 0.002272563, + 0.000404511, + 0.053488024, + -0.025575787, + -0.01793017, + 0.052778743, + -0.047605176, + 0.02958113, + -0.0070406427, + 0.00027184704, + -0.015040899, + -0.012297655, + 0.057868868, + -0.02993577, + -0.00475113, + -0.003971444, + 0.0039401525, + 0.0006193158, + -0.042556774, + -0.045685947, + 0.024887368, + -0.017158307, + 0.02062126, + 0.025367174, + -0.026744012, + 0.023176752, + 0.022154555, + -0.025367174, + -0.056199975, + -0.012589713, + -0.06312588, + 0.021591304, + 0.019129686, + 0.002250398, + -0.0071240873, + -0.030978829, + -0.031604663, + 0.06337622, + 0.016167402, + -0.058160923, + 0.03694512, + -0.00932494, + 0.03316925, + 0.0114945015, + 0.045477338, + -0.018566435, + 0.005434333, + 0.0077707833, + 0.013434589, + -0.034170587, + 0.006978059, + -0.020485662, + 0.019932842, + -0.014018702, + -0.027265541, + 0.006524329, + -0.040449794, + -0.03118744, + -0.011932585, + -0.018357825, + 0.036819953, + 0.006649496, + -0.0042687156, + -0.06713122, + 0.01595879, + -0.0036402731, + -0.005449979, + 0.03746665, + -0.0092310645, + 0.039093822, + 0.00014317603, + -0.021257525, + 0.012986074, + -0.0005189214, + 0.048356175, + -0.039511044, + 0.012422822, + -0.0678405, + -0.008177576, + 0.00046905022, + -0.024678756, + -0.004513834, + -0.0032439108, + 0.01738778, + -0.017450362, + 0.011713543, + 0.017982323, + -0.002516378, + 0.0137683675, + -0.006764232, + 0.0019648608, + -0.020120593, + -0.0487734, + 0.017638113, + 0.00023762169, + 0.03694512, + -0.069759734, + 0.0012073398, + 0.021549582, + 0.01990155, + -0.021779055, + 0.0017340842, + 0.03031127, + 0.0018762009, + -0.027536735, + -0.032355666, + 0.0050718705, + 0.015572859, + -0.02117408, + 0.007489158, + 0.07205446, + 0.039886545, + 0.004177448, + -0.038864348, + 0.035025895, + -0.012224642, + -0.02920563, + 0.014800996, + 0.016386444, + 0.010138526, + 0.0055855764, + 0.0044147437, + 0.03982396, + -0.06074771, + 0.03331528, + -0.026952622, + -0.007134518, + 0.0175651, + 0.01953648, + 0.007103226, + -0.040428936, + -0.005118808, + 0.03081194, + -0.008939008, + 0.02367742, + 0.02403206, + 0.015771039, + -0.037570957, + -0.0013494565, + 2.0983396e-05, + -0.009674365, + -0.034483504, + 0.039844822, + 0.0023781727, + -1.9944413e-05, + 0.033795085, + -0.0027119513, + -0.022342306, + -0.02490823, + 0.018514283, + 0.037758704, + -0.0071449485, + -0.025200285, + 0.016334292, + -0.013434589, + -0.001198865, + -0.04697934, + -0.022863835, + -0.020214468, + -0.021612165, + 0.007916812, + -0.020767288, + 0.0018631626, + 0.014383772, + 0.044768058, + 0.021382693, + -0.049065456, + -0.026472816, + -0.0001677042, + -0.013747507, + -0.012308086, + -0.029247351, + -0.023281058, + -0.03836368, + -0.023030724, + 0.0019009735, + -0.031646386, + 0.0014302935, + 0.016814098, + -0.07547569, + 0.040282905, + 0.018973228, + -0.026848318, + -0.06078943, + -0.02294728, + 0.03638187, + 0.02205025, + 0.04251505, + 0.029080462, + 0.016803667, + -0.04518528, + -0.03853057, + 0.043891888, + -0.009721302, + 0.05052574, + -0.020402217, + 0.02278039, + 0.0092310645, + -0.009022453, + -0.023260197, + 0.014790565, + 0.017961461, + 0.026994346, + 0.038301095, + -0.0046442165, + 0.036298424, + 0.059621204, + -0.018670741, + 0.02745329, + -0.009643073, + 0.019609494, + 0.006774663, + -0.0043651983, + -0.02634765, + 0.052778743, + -0.029873187, + 0.024428422, + 0.003700249, + 0.052945632, + 0.013298992, + -0.012339378, + -0.044225667, + -0.005309166, + 0.017116584, + 0.045769393, + -0.016814098, + 0.015656304, + -0.017262613, + 0.059829816, + 0.015812762, + -0.01829524, + -0.021820776, + -0.030937105, + -0.032126192, + -0.024261532, + 0.016209124, + -0.015572859, + -0.014029132, + -0.033127528, + -0.035630867, + 0.013883105, + 0.006399162, + 0.014654967, + -0.0070406427, + -0.01397698, + -0.02382345, + 0.003009223, + -0.016063096, + 0.031542078, + 0.025534064, + 0.043349497, + -0.052194633, + -0.0021943336, + 0.010414936, + 0.03673651, + 0.052278075, + -0.010806083, + -0.012339378, + -0.018587297, + -0.016532471, + -0.03064505, + -0.030019214, + -0.031896718, + -0.06358483, + -0.044559445, + 0.038071625, + -0.032731164, + -0.05845298, + 0.025784397, + 0.024783062, + -0.023030724, + -0.036924258, + 0.012767032, + -0.020308342, + -0.015948359, + 0.007510019, + 0.0218625, + 0.025784397, + -0.05778542, + 0.042640217, + 0.026806595, + 0.04531045, + 0.022676084, + -0.021121928, + 0.03515106, + -0.064127214, + 0.013319853, + 0.008824272, + 0.030937105, + -0.03498417, + -0.01918184, + -0.008803411, + 0.037404068, + 0.030707633, + 0.0042452468, + 0.026285065, + 0.017878016, + -0.019849397, + 0.022363167, + -0.014425495, + 0.017554669, + 0.11214962, + -0.04556078, + -0.030770216, + -0.0064304536, + -0.017554669, + -0.027515875, + 0.0315838, + 0.035088476, + -0.0071553793, + -0.008109777, + -0.04051238, + 0.02759932, + -0.031250022, + -0.021570442, + -0.011619668, + -0.018357825, + -0.019588633, + 0.01540597, + 0.058035757, + 0.06842462, + -0.040053435, + -0.006920691, + 0.010649624, + -0.012433253, + -0.066839166, + 0.0448515, + 0.0140082715, + 0.031124856, + 0.014018702, + -0.022175416, + 0.035463978, + -0.047980677, + 0.041680604, + 0.016240416, + -0.035255365, + 0.014216883, + 0.037278898, + -0.036840815, + -0.0060497373, + -0.0072544697, + -0.024991674, + -0.048314456, + 0.01901495, + 0.01095211, + -0.018670741, + -0.0009909052, + 0.037612677, + 0.02920563, + -0.042473327, + -0.025179425, + -0.018170074, + -0.037007704, + 0.0034525224, + 0.005118808, + -0.04163888, + -0.013006936, + -0.013883105, + 0.059203982, + -0.0026963053, + 0.0023846917, + -0.016000513, + 0.015739748, + 0.020569107, + 0.034295753, + 0.0019687724, + 0.008579154, + -0.0055177775, + -0.0044017057, + 8.3526145e-05, + 0.04806412, + -0.0042243856, + -0.012996505, + -0.0100237895, + -0.0030431221, + 0.0398031, + 0.0243867, + 0.010879097, + 0.05286219, + 0.0024603135, + -0.01739821, + 0.02205025, + 0.0056950976, + -0.02655626, + -0.017898878, + -0.02976888, + -0.017252183, + -0.001397698, + -0.037758704, + -0.024303256, + -0.013705784, + 0.006779878, + 0.0051735686, + 0.0026858747, + 0.014143869, + 0.015385108, + 0.00048730374, + -0.0023938185, + -0.026431095, + 0.03141691, + -0.039323293, + -0.012662726, + 0.05019196, + -0.003118744, + 0.027202956, + -0.023072448, + 0.020287482, + -0.013382437, + -0.0015372069, + 0.03356561, + 0.009580489, + 0.015332955, + -0.0067850933, + -0.01039929, + -0.02064212, + 0.012162059, + 0.0103001995, + 0.02365656, + -0.012787893, + -0.035756033, + 0.031458635, + -0.01488444, + -0.0055386387, + -0.0007809898, + 0.015990082, + -0.020892454, + -0.014049994, + 0.06078943, + -0.0030405147, + -0.042222995, + -0.00048599992, + 0.011338042, + 0.048356175, + -0.0014993962, + -0.027724486, + -0.0036898183, + -0.021382693, + -0.026055593, + -0.0027771425, + 0.05019196, + 0.0265354, + -0.014686259, + -0.018013615, + 0.019494757, + -0.012255933, + 0.022613501, + 0.006039307, + 0.027536735, + -0.015218219, + -0.013862243, + 0.0006646236, + -0.014362911, + -0.00932494, + -0.0015385108, + -0.010534887, + -0.04301572, + -0.0032491263, + 0.013121672, + -0.006211411, + -0.0059558623, + 0.032397386, + 0.060372207, + 0.02130968, + 0.01237067, + 0.017898878, + -0.013048658, + -0.014863579, + 0.02190422, + -0.024344977, + 0.0022921204, + 0.05899537, + -0.016459458, + -0.046478674, + -0.01900452, + -0.027348986, + 0.021591304, + -0.013173825, + 0.019400882, + -0.018921075, + -0.011087708, + 0.0071762404, + 0.040199462, + -0.028767545, + 0.0362567, + -0.033795085, + -0.0261599, + -0.04261936, + -0.041325964, + 0.020746427, + -0.007082365, + 0.012235072, + 0.015687594, + 0.022676084, + 0.00046448683, + 0.03102055, + -0.026493678, + -0.0056533753, + 0.0110668475, + -0.045227002, + -0.013205117, + -0.0043938826, + 0.018034475, + -0.013757938, + -0.021758193, + -0.015875345, + -0.028079126, + -0.008380973, + -0.03907296, + -0.03243911, + 0.025596648, + 0.0137683675, + -0.015155635, + 0.01595879, + 0.017700696, + 0.012767032, + -0.032522555, + 0.021674749, + -0.017606823, + -0.001331855, + -0.0046103173, + 0.0061279666, + -0.012485406, + -0.033294417, + -0.029518547, + 0.03913554, + 0.0038671382, + 0.0145610925, + -0.0014902693, + -0.042598497, + -0.032188777, + -0.041138213, + -0.0073379143, + 0.0047615604, + -0.010519242, + 0.0012992594, + -0.015499844, + -0.0033195326, + 0.013601478, + -0.01524951, + -0.048523065, + 0.015979651, + -0.012120336, + 0.029601991, + 0.0032725951, + 0.06012187, + 0.0034707761, + -0.00052120315, + -0.019828536, + -0.009173697, + 0.029122185, + -0.013372006, + -0.0025111625, + -0.042807106, + -0.02455359, + 0.021466138, + -0.0061071054, + 0.021153219, + 0.027933098, + 0.02062126, + 0.029539408, + 0.003507283, + 0.031437773, + 0.0045086187, + 0.009194558, + -0.024678756, + 0.00015189535, + 0.012182919, + 0.0024159835, + -0.023218475, + 0.008500924, + 0.0075986786, + -0.00056814076, + -0.016960125, + 0.012829616, + 0.01684539, + 0.023239337, + -0.013465881, + 0.025846982, + 0.026326789, + 0.018587297, + -0.031875856, + -0.026451955, + 0.053237688, + -0.0130277965, + 0.009940345, + 0.0304573, + -0.00073796365, + -0.034462642, + -0.0029283857, + -0.047938954, + 0.048272733, + -0.043182608, + -0.046895895, + -0.004036635, + -0.008641737, + 0.024261532, + 0.00065810455, + 0.0125375595, + -0.015520706, + 0.0052674436, + 0.029393379, + -0.038321957, + 0.021883361, + 0.04001171, + 0.005522993, + -0.04572767, + 0.008292313, + -0.010722638, + -0.026973484, + 0.027286401, + -0.02150786, + 0.027641041, + -0.019223562, + -0.01955734, + -0.03498417, + -0.011233737, + -0.008370542, + -0.027787069, + 0.007723846, + 0.0049467036, + -0.066839166, + -0.0145298, + 0.02419895, + -0.014185592, + -0.032877192, + -0.024240673, + -0.0055855764, + 0.032397386, + 0.0018775048, + 0.009935129, + -0.048648234, + -0.0006995009, + 0.02221714, + -0.0055490695, + -0.025513204, + 0.018243087, + 0.02043351, + 0.053362858, + -0.008083701, + -0.028183432, + -0.0053195967, + 0.012235072, + -0.032606, + -0.008792981, + -0.0028292954, + -0.02889271, + -0.04072099, + -0.022634363, + -0.022342306, + -0.012829616, + 0.02816257, + 0.020464802, + 0.028558932, + -0.025930427, + -0.03462953, + 0.013184255, + 0.003906253, + -0.018576866, + 0.0023912108, + -0.0020561283, + 0.029622853, + -0.0132677, + -0.000757521, + -0.008182791, + -0.039323293, + 0.02707779, + 0.007384852, + -0.004456466, + 0.032752026, + -0.02116365, + 0.011734405, + 0.023218475, + 0.012506267, + -0.012631435, + -0.02812085, + -0.0304573, + 0.035046756, + 0.008287097, + -0.007833367, + -0.026285065, + 0.046895895, + -0.03711201, + -0.010013359, + 0.0075621717, + 0.060163595, + 0.05144363, + 0.0014798387, + -0.0032308728, + -0.005296128, + 0.016417736, + 0.032522555, + 0.05970465, + -0.029393379, + -0.03569345, + -0.014738412, + -0.0043521603, + 0.03694512, + -0.01076436, + 0.014654967, + -0.004148764, + -0.0019531264, + -0.010649624, + -0.0008950743, + 0.031083133, + 0.01881677, + 0.020329203, + 0.015353817, + -0.016407305, + 0.016741084, + -0.033586472, + 0.02490823, + -0.028621517, + -0.0027484582, + 0.0015424222, + -0.008005472, + 0.01182828, + 0.02152872, + 0.014508939, + 0.029601991, + 0.025596648, + -0.0040992186, + -0.0016897542, + -0.012944352, + 0.010837374, + -0.015979651, + -0.020735996, + -0.04606145, + -0.037049428, + 0.016741084, + 0.018900214, + 0.040741853, + -0.01595879, + 0.039010376, + 0.005893279, + 0.006743371, + 0.020861164, + -0.025721814, + -0.0401786, + -0.028642377, + 0.016928835, + 0.013914396, + -0.0057420353, + -0.044517722, + 0.052111186, + 0.043474663, + -0.004910196, + 0.007603894, + 0.042222995, + -0.010253262, + -0.023427086, + 0.025700953, + -0.019369591, + -0.044058777, + 0.015353817, + -0.035610005, + 0.0019531264, + -0.025492342, + 0.042473327, + 0.0074839424, + -0.0065660514, + 0.035547424, + 5.390492e-05, + -0.017346058, + 0.013914396, + -0.008699105, + 0.01738778, + -0.054280747, + 0.0005932393, + 0.011703113, + -0.011765696, + -0.016459458, + 0.015270372, + 0.026305927, + -0.032334805, + -0.03191758, + -0.014905301, + 0.0009465753, + -0.015802331, + 0.018889783, + -0.010774791, + -0.0007875089, + -0.019056672, + 8.242604e-05, + -0.018952368, + 0.01128589, + -0.02096547, + 0.018889783, + -0.020391786, + -0.0011890864, + 0.017888447, + -0.037550095, + 0.00059910654, + 0.00516053, + 0.02096547, + 0.041159075, + 0.017366918, + 0.008433125, + 0.014581953, + 0.0047250534, + -0.0005257665, + -0.0037889087, + -0.021048915, + -0.0034108001, + 0.02651454, + -0.017366918, + -0.003236088, + -0.048189286, + -0.006800739, + -0.02294728, + -0.0027641042, + 0.004464289, + -0.0066077737, + -0.050901238, + 0.0075934636, + -0.002109585, + 0.002860587, + 0.0007294888, + 0.0017953638, + 0.003934937, + -0.0061488277, + 0.027140373, + 0.02636851, + -0.020715136, + 0.0038593153, + 0.030186104, + -0.0072701154, + 0.03944846, + -0.013184255, + -0.0062844255, + 0.0075778174, + -0.042556774, + 0.016083958, + -0.04643695, + 0.005642945, + 0.013664062, + -0.018973228, + -0.052027743, + -0.03264772, + 0.007885519, + -0.037779566, + 0.025721814, + 0.02026662, + 0.026744012, + -0.004052281, + -0.006164474, + 0.052194633, + -0.04180577, + 0.026827456, + -0.023886032, + 0.0019361768, + 0.0012973036, + 0.019035812, + -0.0034160155, + -0.011254597, + -0.01956777, + -0.016939266, + -0.024157228, + -0.0020052793, + 0.018785479, + 0.058703315, + -0.015906638, + 0.02889271, + -0.0038853916, + -0.0066025583, + 0.002500732, + 0.0016206517, + -0.02655626, + 0.03921899, + 0.005429118, + 0.00045992347, + 0.03314839, + -0.015301663, + 0.013184255, + -0.003163074, + 0.018535145, + 0.032835472, + -0.029873187, + -0.019202702, + -0.039865684, + -0.016094388, + -0.019087965, + -0.044934947, + 0.021320108, + 0.005155315, + 0.021184511, + 0.0476469, + -0.002052217, + 0.051777408, + 0.01687668, + 0.0243867, + -0.0401786, + -0.013246839, + -0.012829616, + 0.013163394, + -0.0027354201, + -0.018722894, + 0.0025124664, + 0.02078815, + 0.018503852, + -0.043516386, + -0.00086378254, + -0.028204294, + 0.010712207, + -0.0261599, + 0.012683587, + -0.037758704, + -0.019254854, + -0.00057563774, + -0.014811426, + 0.0008709536, + -0.02509598, + 0.028955296, + 0.009888192, + 0.018868923, + 0.030165242, + -0.02313503, + 0.008798196, + 0.022842973, + 0.03444178, + -0.010691347, + -0.0015450299, + -0.0084279105, + 0.01093125, + -0.051735684, + -0.02259264, + 0.0045555565, + 0.030102659, + 0.00039734, + 0.021987665, + -0.03732062, + 0.027870515, + -0.02419895, + -0.023281058, + 0.020172745, + -0.056158252, + 0.028475488, + -0.00094918296, + -0.033106666, + 0.0040913955, + 0.03177155, + -0.0092310645, + -0.0008364023, + 0.009961206, + 0.035881203, + -0.022133695, + 0.017095724, + -0.008323604, + -0.012057752, + 0.027307263, + -0.046645563, + 0.0040340275, + 0.03366992, + 0.017773712, + -0.027390707, + -0.033357, + 0.0024238063, + 0.0156041505, + 0.007103226, + 0.02294728, + 0.0140082715, + -0.054197304, + 0.002302551, + -0.033127528, + 0.029455964, + 0.008255806, + -0.03871832, + 0.0030744139, + 0.022905558, + -0.01921313, + -0.019838966, + -0.0006629939, + -0.029685436, + 0.010879097, + 0.0041591944, + 0.00064311054, + -0.021247094, + 0.00014643559, + -0.0064878217, + 0.034879867, + 0.027828792, + -0.032689445, + -0.033607334, + -0.022488335, + 0.0015776255, + -0.02386517, + 0.0020157099, + -0.034712978, + 0.0017510339, + -0.019192271, + -0.027411569, + 0.022801252, + -0.012464545, + -0.0039688363, + 0.011045986, + -0.01524951, + 0.0243867, + -0.025221147, + -0.016949695, + 0.012036892, + -0.010795652, + -0.048523065, + -0.014550662, + 0.0031474282, + -0.0126001425, + 0.018138781, + 0.0022999432, + -0.001809706, + -0.033294417, + 0.0014694082, + 0.002500732, + 0.0063626547, + 0.013830951, + -0.00824016, + -0.044017054, + 0.017147876, + -0.01795103, + 0.0072336085, + -0.028433766, + 0.005079693, + 0.013236408, + 0.02169561, + 0.053571466, + 0.008010686, + -0.014842718, + 0.017283473, + -0.012881769, + 0.037570957, + -0.0029883618, + 0.00524919, + -0.033273555, + 0.021779055, + -0.033252694, + 0.06012187, + 0.0034081927, + 0.010712207, + 0.011796988, + -0.00295707, + 0.02580526, + 0.01182828, + 0.0021721686, + -0.0038071624, + 0.009830823, + 0.011932585, + -0.008433125, + -0.025763538, + 0.0054134717, + 0.03606895, + 0.02257178, + 0.00904853, + 0.019995425, + 0.004941488, + -0.023927754, + -0.013851812, + -0.012068183, + 0.0065869126, + -0.0007699073, + -0.017648544, + 0.008751258, + 0.025075119, + -0.033085804, + 0.04443428, + 0.011390195, + -0.0002037223, + 0.012339378, + 0.0026219876, + 0.051318463, + -0.00022197582, + -0.04266108, + 0.017095724, + 0.024073783, + -0.01183871, + -0.029143045, + 0.011202445, + 0.010034219, + -0.017450362, + 0.024532728, + -0.027286401, + 0.045227002, + -0.050150238, + -0.014842718, + -0.048147567, + 0.005215291, + -0.010691347, + 0.022738669, + 0.021549582, + 0.022196278, + -0.014894871, + -0.011859572, + 0.009283218, + 0.03049902, + 0.011880432, + 0.039531905, + 0.011849141, + -0.0068841837, + -0.016073527, + -0.0159275, + 0.021549582, + 0.012099475, + -0.0032752026, + -0.008208868, + 0.016042234, + 0.009914268, + 0.02099676, + 0.02582612, + -0.004743307, + -0.0025268085, + 0.02134097, + 0.0137683675, + -0.0054604094, + 0.024845645, + 0.013664062, + -0.024511866, + -0.04985818, + -0.015134774, + -0.009721302, + 0.00922585, + -0.020318773, + 0.00860523, + -0.016417736, + -0.0037315406, + -0.03711201, + -0.002494213, + 0.00012321124, + -0.013069519, + -0.010920819, + 0.009220635, + -0.012589713, + -0.004399098, + -0.013215547, + -0.009888192, + -0.023698281, + -0.0056742365, + -0.031542078, + -0.0024407562, + -0.002464225, + 0.0040940032, + 0.022968141, + -0.013935258, + -0.034733836, + -0.010122879, + 0.025304591, + -0.012287226, + -0.01290263, + -0.022467474, + -0.019849397, + -0.005272659, + 0.010055081, + 0.0039375448, + -0.004860651, + -0.0015698025, + 0.0041670175, + 0.026013872, + -0.0047667758, + 0.011661391, + 0.015353817, + -0.028538072, + -0.05857815, + -0.033649057, + 0.018347394, + -0.0030744139, + -0.036840815, + -0.02599301, + -0.006378301, + -0.016167402, + -0.017074862, + 0.028392043, + -0.015854483, + 0.009997712, + -0.0055855764, + -0.007671693, + 0.012495837, + 0.0064669605, + -0.00089311856, + 0.013883105, + 0.041430272, + -0.017158307, + 0.02007887, + 0.05841126, + 0.020423079, + -0.009877761, + 0.0030561604, + -0.0026089493, + -0.0017080078, + 0.004899766, + 0.035359673, + 0.0074317893, + -0.0033560398, + -0.024324117, + 0.027995681, + -0.0023051586, + -0.02386517, + 0.0041070418, + 0.024532728, + 0.0007157987, + 0.005476055, + 0.025346315, + 0.050650906, + -0.013111241, + 0.0019009735, + -0.023364503, + -0.0019453035, + -0.006581697, + -0.026869178, + 0.0015463338, + 0.0021460922, + 0.005642945, + -0.0057107434, + -0.029831463, + 0.007489158, + 0.016907973, + -0.024011198, + -0.0011056417, + -0.018159643, + -0.00051240233, + 0.023614837, + 0.013997841, + 0.021779055, + -0.024136366, + -0.010628763, + 0.010712207, + -0.020287482, + 0.03402456, + 0.011317181, + -0.01307995, + 0.018670741, + 0.0025528849, + -0.021716472, + 0.02133054, + -0.016167402, + 0.022884697, + 0.0010828248, + -0.02993577, + 0.042389885, + -0.003801947, + 0.015812762, + -0.0066964333, + -0.020068439, + -0.017304335, + -0.016261278, + 0.03590206, + 0.0058776326, + 0.002831903, + -0.007864659, + -0.023281058, + 0.014425495, + -0.0013677101, + 0.021278387, + 0.030937105, + 0.009027668, + 0.016970556, + -0.014237744, + -0.009158051, + -0.022968141, + 0.015990082, + 0.00027494362, + 0.0065921275, + -0.006383516, + 0.011796988, + -0.0020509132, + 0.0027693193, + 0.04572767, + 0.014748842, + -0.0028371182, + -0.035756033, + 0.0076873386, + 0.005303951, + -0.013716215, + 0.009815178, + -0.00070732384, + -0.013726645, + -0.004469504, + -0.018931506, + 0.02795396, + 0.007901166, + -0.018368255, + 0.036653064, + 0.026597984, + -0.0006352876, + 0.032606, + -0.00076078053, + -0.0038853916, + -0.015510275, + 0.01864988, + -0.020579537, + 0.0045060115, + 0.039490182, + 0.043140884, + -0.04501839, + 0.0061801197, + -0.007374421, + 2.9132289e-05, + 0.01881677, + 0.004545126, + -0.0027328124, + 0.011932585, + 0.052820466, + -0.03644445, + 0.02795396, + 0.0026272028, + -0.008761689, + 0.016814098, + 0.011119, + 0.020047579, + 0.014811426, + 0.021257525, + -0.05019196, + -0.0024668325, + 0.024282394, + -0.009095468, + 0.033628196, + 0.018597728, + -0.002206068, + -0.014654967, + -0.009335371, + 0.011087708, + 0.0026754441, + 0.045852836, + -0.0145610925, + -0.0040209894, + 0.023281058, + -0.016647208, + -0.01917141, + -0.042306438, + 0.02958113, + -0.0019387844, + -0.038384542, + 0.0041722325, + -0.03208447, + 0.03999085, + -0.008646952, + -0.0067694476, + -0.0009909052, + 0.005721174, + -0.027661903, + 0.033231832, + 0.026848318, + -0.028663239, + -0.039740518, + -0.0125375595, + -0.035171922, + 0.001448547, + -0.017283473, + 0.015166066, + 0.016511612, + 0.003619412, + 0.0616656, + 0.011025125, + -0.016563764, + -0.027474152, + -0.03723718, + -0.014498509, + -0.0121724885 ], "prompt": "Create a hello world application using C#", "language": "C#" @@ -20099,7 +20099,7 @@ }, { "name": "rust-linked-list", - "codespaces": "\n\n=== ./.devcontainer/devcontainer.json ===\n{\n \"name\": \"Rust Linked List\",\n \"build\": {\n \"dockerfile\": \"Dockerfile\"\n },\n \"customizations\": {\n // Configure properties specific to VS Code.\n \"vscode\": {\n \"settings\": {},\n \"extensions\": [\n \"rust-lang.rust-analyzer\"\n ]\n }\n },\n \"postCreateCommand\": \"rustup update && rustup component add rustfmt\",\n \"remoteUser\": \"root\"\n}\n\n=== ./.devcontainer/Dockerfile ===\nFROM mcr.microsoft.com/devcontainers/base:ubuntu\n\nENV DEBIAN_FRONTEND noninteractive\n\nRUN apt update && \\\napt install -y curl && build-essentials \\\ncurl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y", + "codespaces": "\n\n=== ./.devcontainer/devcontainer.json ===\n{\n \"name\": \"Rust Linked List\",\n \"build\": {\n \"dockerfile\": \"Dockerfile\"\n },\n \"customizations\": {\n // Configure properties specific to VS Code.\n \"vscode\": {\n \"settings\": {},\n \"extensions\": [\n \"rust-lang.rust-analyzer\"\n ]\n }\n },\n \"postCreateCommand\": \"rustup update && rustup component add rustfmt\",\n \"remoteUser\": \"root\"\n}\n\n=== ./.devcontainer/Dockerfile ===\nFROM mcr.microsoft.com/devcontainers/base:ubuntu\n\nENV DEBIAN_FRONTEND noninteractive\n\nRUN apt update && \\\napt install -y curl && build-essential \\\ncurl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y", "readme": "\n\n=== ./README.md ===\n# Rust Linked List\n\nThis project is a linked list written in Rust.\n\n## Setup\n\nTo set up the project, you will need to run the following commands:\n\n```\nrustup update\nrustup component add rustfmt\n```\n\n## Running the App\n\nTo run the app, you will need to run the following command:\n\n```\ncargo run\n```", "code": "\n\n=== ./Cargo.toml ===\n[package]\nname = \"linked_list\"\nversion = \"0.1.0\"\nauthors = [\"Your Name \"]\n\n[dependencies]\n\n=== ./src/main.rs ===\nuse std::rc::Rc;\nuse std::cell::RefCell;\n\n#[derive(Debug)]\nstruct Node {\n data: T,\n next: Option>>>,\n}\n\nimpl Node {\n fn new(data: T) -> Self {\n Node {\n data,\n next: None,\n }\n }\n}\n\n#[derive(Debug)]\nstruct SinglyLinkedList {\n head: Option>>>,\n tail: Option>>>,\n length: usize,\n}\n\nimpl SinglyLinkedList {\n fn new() -> Self {\n SinglyLinkedList {\n head: None,\n tail: None,\n length: 0,\n }\n }\n\n fn push(&mut self, data: T) {\n let new_node = Rc::new(RefCell::new(Node::new(data)));\n\n match self.tail.take() {\n Some(old_tail) => {\n old_tail.borrow_mut().next = Some(new_node.clone());\n }\n None => {\n self.head = Some(new_node.clone());\n }\n }\n\n self.tail = Some(new_node);\n self.length += 1;\n }\n}\n\nfn main() {\n let mut list = SinglyLinkedList::new();\n list.push(1);\n list.push(2);\n list.push(3);\n println!(\"{:?}\", list);\n}\n", "embedding": [ diff --git a/src/AzureExtension/QuickStartPlayground/Completions.cs b/src/AzureExtension/QuickStartPlayground/Completions.cs index f3d3040b..812e8fec 100644 --- a/src/AzureExtension/QuickStartPlayground/Completions.cs +++ b/src/AzureExtension/QuickStartPlayground/Completions.cs @@ -16,6 +16,9 @@ public static async Task GetDevContainerFilesAsync(IAzureOpenAIService a Remember that VS Code dev containers automatically copy over the source code from the input folder to the repo, and do not need COPY commands. In your Dockerfile do not include any COPY commands. +When filling in the RUN command, if there are multiple libraries to install, put them all into the same package manager install command. +In your Dockerfile do not have multiple package manager install commands in the RUN section and do not duplicate packages. + Any commands to install requirements based on repo files must be put in the devcontainer.json file as a 'postCreateCommand'. For example, do NOT put a `COPY requirements.txt .` line in the Dockerfile. Put a `'postCreateCommand': 'pip3 install -r requirements.txt'` line in devcontainer.json. Please pick the best language and best tools and frameworks necessary to match the prompt."; From c557ea29fac26b6726bbe5d3306539b00115be55 Mon Sep 17 00:00:00 2001 From: ssparach <128866445+ssparach@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:07:16 -0700 Subject: [PATCH 09/11] changes for package update (#236) --- src/AzureExtension/AzureExtension.csproj | 3 ++- src/AzureExtension/DeveloperId/AuthenticationHelper.cs | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/AzureExtension/AzureExtension.csproj b/src/AzureExtension/AzureExtension.csproj index b1eb6a14..a7893f70 100644 --- a/src/AzureExtension/AzureExtension.csproj +++ b/src/AzureExtension/AzureExtension.csproj @@ -66,7 +66,8 @@ - + + diff --git a/src/AzureExtension/DeveloperId/AuthenticationHelper.cs b/src/AzureExtension/DeveloperId/AuthenticationHelper.cs index e2895a44..be28c987 100644 --- a/src/AzureExtension/DeveloperId/AuthenticationHelper.cs +++ b/src/AzureExtension/DeveloperId/AuthenticationHelper.cs @@ -3,6 +3,7 @@ using System.Globalization; using Microsoft.Identity.Client; +using Microsoft.Identity.Client.Broker; using Microsoft.Identity.Client.Extensions.Msal; using Microsoft.IdentityModel.Abstractions; using Microsoft.UI; From faf0a64288e48ec991d06ddeaa65d8d644022e1c Mon Sep 17 00:00:00 2001 From: Branden Bonaby <105318831+bbonaby@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:17:04 -0700 Subject: [PATCH 10/11] Update devbox icon (#235) --- src/AzureExtension/Assets/DevBoxProvider.png | Bin 692 -> 9170 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/AzureExtension/Assets/DevBoxProvider.png b/src/AzureExtension/Assets/DevBoxProvider.png index 2c6b337587d79822d0f5771274f4e97e1737e68f..54d2549d47b0b3c50c75a5789a8d956ca510748c 100644 GIT binary patch literal 9170 zcmXw9cRZV4)PE8ycFa(E-P{|siZ5?9n?-7D<333Mz2!yz|pHHx>OMtt$e~@Rvj`B4C z-~#lt)bEBBZqFkkum3Gl+Z&US`E0vgVFn8Y^_9HBkv6)-vg*1aS=sI1H9ydbV!!7 z`(>b4U`XK^2nEy2E)J}lx1rVa5}x#F z(0!Ejk|VAn3V5|mP4bC6j5EQ4!bP_5Vxxb8GUL_umG#dh@G}irpGBzOuGu__FBL`4 zV~}%w5Ljb}KZ<4Czh~a&)3U5Qe6JO_p$FVC^ug1c^}?vS`M`qNMMA={AHhe;31<&n z+b)q?w+vLp0dmC%J5N)=wF|3ft#qZVjfYgG5R}vHveyOQREFG>T@C_3Eamw>QFjF| z47JWEfQ&8WK^{yL_Zr^t!6A0PO>wR9uwnQQ82Z{qtRt|-V@~w8JsFpAXz?UNK_bfG z3Dfy;^amlHZ`}~RY6Wtuf6w6go`o@@ z1kpCq!%!6GT?_p04c0_m0*&<^5@%?zJaN@pD((i-cyN@50mKe%ZGO{zm9-@*s?rJ+ zmWNlIDgqg?&m@%6w4q2Rl7!34I;OSf>`9f(FyxYOK}L2p&1yZQ)gY8PZI(OXtffB{ zJJlH9Fu4c@oJVyQ?Bmyr0sg_!dx6n$tu@{rIQ#%jx0z-pJG{9@Zef+$gsAoKYY!iM z?B|Db4tgy5ePSU8X!65Fhh`iHNlb3C0T7e45T)%Z`=zm|q4%#FsPzlS#tFslbKqTe z5#LZdJ9NkRaM}6fTbo!3@PeJ`w^O`QjId}()@inkTsb@xnv+{741i69e|_oX$J919 z%_IWM%bB|phYL$RRcJv^3MBBNMs8OI+^P}Ed|Ad9%d<;4(Tv`|MjkPg2R4~niGZTA z8-r(NNVL0fc>JQ+h_&3nW3Wm4d;A@wUuIFy$?eFp#=4T1!OZWkOpzN;aK>@z(=J2$ zVPbdaloPg%izLdnA2hSQsp7J+3L;W%oc}GsK;pYXW%3CcW0Kd zpw*Z6ScERpl!d&an#F*5-gX4JbL`f2g|q+<)9(>4w^)V?#SowxxwA2bntf78&6cQw z_*=-ZgZZMPfD(kQa9anMXy!IHJ$l`CXcy-JjW%zRdqS(}qW9hbBuEvu%at%(w^HqapDWSxb$3Z8KUV{{btDv^nJ$@&7Ic`R8oF2 z4~3irHnS3pb0WdZ?U-E@ugzU5;u5uz-$c35yr#tZgD^gZIzIW8p-8(1S6c|B5>KW$(GY|=CsaSa3;8tqEjQtSv!k#&0r z;$mL_xF?Y&PsFO^2lozjq^1Z-s~0Tv7%8I>eosIGfU$9~lJjH8#B`CSMu? za6(Et@0#2|)en$A<$uU$P}yq4G4mQ1ig6u~%5e_U5HD(gAb+xjvn!v1hkwt=oAd57 z@z{f$KWr(Bj@+Jnt1{7U8Jcd-A4P2P@tyyxav>*y*!mi1NrREXJ#T>kIzSwt*AoGn zY_ZQRwDIo(QhXrGIoa`pS|y;8UOtO{1jwU)SH;01`x3Ax-7{1QDlGHvubz^j$oNK+ zRVytAuJ%>6ELXhVQd4u_u*dh=7fDYclnvwz5n6vlbD;hnoA}IL_?g`NN~U!NYCUXv z4k58fhK*|Y8gKLra~&I$@GxG%U>9S zE#){xOmA+>BhOyGz&U$Z>A?4yTP)MJp0nS8RffPyt3F25n6OLGmGNY&y+r5#(ZUe} zZ0=-(+Cslbs(m*doS&U=po&VUpFj?6N*Z6i0`7hcucuFH1fvG-VwH33Ams+3N%q+x zplaWJN1}2k5cUX`!@?a#%%yKeQd%<=@ez7-w^1$muN;_Ar`N3u5|$6^4(JkU`ia2ZW>)$pF|Gt}VBtlO; zql6o#wS0qqphpU*WU^f5%F&1Y1pTGOMs`hJGXqtlmsLg}6PG}Lr3AgJDJyUdax}Ce zLs|SfwzKI*uLE!&(KR*y>oYwx_($R6_hG(#<+>WSi@UZW&=>6dc75%%3-iA&0YtP1 z_iIY-=)aSf%~pPEc=`noWvRd?K#g6vnx4i@q`+0K`}j3r!CSFuPX(>ls5eaT+veJt zfLpeJ!GOVyetHk+_!3`VVoQB4ap#sKnrnr5KENv@UP*0;CKdXpf2X+P)JN{9L`ca% zPLdsTxA|lgmK4VJC8n=2gEN7xz=H{E777(fQv^ei(4+$vg88?W<4`e{?{*_aJ zyLG}FkkZCVJO$|Dfya_$4s*S5*>~8r+T@2rH1}lVnUOYP>>NzRVnDx#D!uC)U1;rL zWUPDbO7_XX=smvp9dPxYJ@pQDSclllQF7t@Te_Gog}dkbtPCb(>=oa>1FH**epB!P z(Yu#pGhwZSXj^+WvwR!jz2MH3FUm{hy3UVG9BtU71-HJa=VS4OA=h-s6fj{1C5Bdz)&l{3Cd`D0nCqJgRm(2op127}LhJz+-r+ji>8NOP_x&KWskA#r z5MVt#Z;a^&dP;YR2J*mMHEa<&33RHWN)T7eGQoSH=I(>x1VD8us;H_hXu8c~^^o>0 zs=RVKFMj$+xS(sZRx39B$yunla)1_ZY!I5eF_87^*>|ZtEY4Owy`{1tR*!>As9`s+V~K-xxKW?|jCL zj%}5{NH}=uQ@k$p1WQY;We=3I{1kfz!(2sqa1y^DkzLXO5MZL$VA#+THwV5*B5w~( zihy;!7@d+{(VP{kgP$d6T97$!hWRu+1OoR?-yPJW2n0Ddzp%2RC3*N=7R(NIN!rp% ze?L6ojCn>-ij=}lfE_5IP`(9r;v3vD;FS(N&@FsF_e$uKmmDJpNHQnwgR^6+4JFOR zGw1$?ndrhs@12|YyMt}?G?3Z5aWrY{O40cKO3bC%cmiLE+?XO9SmKfU_%Wf}svS=BL zpt7?Xt!Z9@HqU1dAjX7zsRQlP5%7#3=+J8D@`zIu;D6X9GP zLMb6jsj*VpM4K*=k5OmznDU@l%_#VUBT>B{H*t5Criak>BwoGLlX#Jw)#Ytc3S61= zncvAwSG@(R1z<0!*=1TE+k!YCVx!-G4~7z7sb&PIA?e8p=ozH8nt!biO=otD;-}5y zR4sw#61B>Revw}>{KP@HVK|X88JC?+EXrGZoD%*THWQpaZC+032EcEr2D3 zdmf1VWz?yGdW~h+2kwNy;2qXI_=gdX&PvkN zDFNWp8UntC6XI(cah+ zQ`0I;8I$_;bz{vLQAVLUsa?`f=fM-F4C8ffECX#Gu_9USE#=Cw1;!g#K(fgIV@D}_ z>uzTAY2Eb-xD+dJb5|bvgADHo!fZTOR0Cdoina48Vp%kDTJgP47dTII7M~_kOu9(D z{zhgFGO%vBOyBxwTvsWkcUU^sUt+TK4UgC^`{v%lYZbm>4&g zHJjHONoJxdd~r$ou++WtX_w_lmbdTTbJM$>o`?AVs9uVD&;NbaU(H%0d?q^nRKMg0>L;ab13OT4A895=ITwGJb;=g0=)`&)4EicKqv~mK?S7ua11yVl ztqCVaVt?pL9EliM$aOc`Wu;Db!MS{Re(xn} zJFO^w=dk3y)$3R?E8=lZg9oIqDX^=n4g+b8GY^Sc7qvUrFBL5aHZQCeOlyx)+92Tw z<3(ylY3`DtMg3=PZxc>n{=kBt7kJZJo_Vz!W;f-DN!#wRXN*5id%YM99i?L6dH zH|Cf_7b1s_vbvW@qp((P1pGu6iYqNwf4M5_kX3Ei%0WGL@{lh<-*rEhZG1^jz+fto! ztQvr<6b0NhVvFXwp$VrgUF#-RIbHk%p4!FS4B%;YlG>G5bdh2Wxqb1oKo^K_Hfu?_ z>}juf9?5ueHef*{UQvxF|?MQ9sRy)i)zDLdMQB;Ix=75p0@@4bWqLH)T z! zGKpNYX6Ye+ZVZ*xw$aEQe$omqpgak6qGQRC2pIdJoG!-9>L9lsu6-j!3|_8-Kaw>r z{(5eR+~F#fboDPR73#m~weKMHK#%vSe;dK?PsLBWVBg>k=E{MAs{MJ_GtY1fQ8VLt z9Tn1#@=*`c4kU+`Z@fwo=Um*GzVcGbnb&=MNB4;l))uM7{>9q{zq1Kb@r4M(O7UP* z_=WYrE9!JmV-q!}#_B@k3`%7vYA;E;oJmoRTk_f^9h1V^? zOAn=8zRPn-a!R(dh4dR(dcfl~L({og9ZE4}l=Ffk8CM?*XxCfqel)24d7n0ytEhYt zy4GwJ>9u(qOI%tOR1Izs3+huAA_p$OD3$ul_s3o*D*0h}uzE!Wi2) zZes!%$>QE$MDp63@G<7=qXYm0K$3$fLxL@Hnv*~!sr3J z;!gNHStp5Vt=f5rsz;9Yt}~)zMbrHHo$?T)7$z@Z-oJmr?BKAp{p|D$DEyK3V(8?Rc0u$4;V0% zwo3@bh%X)&n2f1y#W?1feA1qTPJeJeGkLn0^w^RzuJna3&tFOB=7HYD*LOeU#Jhxq zSKv9w!jaR>!Yp>ns5Wy_esVX@Vzu$e>5Za!?sZmj_>?uQDkI>}lLz4K`Ee1;7^roV zyr7Ez7qK`>BrDoS=%15Df4R4JbrCg>8Y&>^{%UMrAU*te_6-N*@go(Q*d`ra{2EC} zX`sLLMNpBkr%>J%YZPtDFf zhf9V1e(rH{`j9B**OAL=My}jktg+!K9HL;e`owC{q{82D`a?2?cF3XlkiXSL<>zsx zy$jLVf+6Z@1t(5z9^i-)K_X6xD_7VzLX1W*cvPP(~n+qfaG6~nxw|`S!x(_A*Ujme178Z5@ru=YybmZ3M^V>=3l=Aa@=w) zv~W=UMs6Y0d&m|pZAQG3jzGB|yz!Z>uaFm9z4N)xd_GrAij#XjQk_N18u2~3=PGOL z)g6#Hz~2~13>|0q{yyAajoC;PIpG$=26*Hsl%;B5y`wI4!IT1dW4O+CksI!95_v2A zkDkw)BJy~sc-|^uddfPscCi2qFt?e*gMfJu`|j4d=JqmpLQfvJwRsbEmn1CI;UX1T z1({&DPX+!x&oFuxgr5wdz5lv(Y4)JAb1aQWZv8(5v?BnN4%NH%y;Bt}u!X$#a3uD~ z2vj#Ej-H+*;-#+G`z9q!aais-6Cjyh@q3sCFHyMI^~1Y3SAd;WD-4kVyA0o+I44X< z%EWS)R4XLBC+vZpgsM!t`Ao9(v9AJyWO3*utbfQZVliXsXjWm#zIA6S>jei7BiPzG=!6qw z-6j*9JfZ5a(^cuo+-uKFN|&>{_O;j*ji?@btGx$7CCqcsgtV%qzmz-19C|YLT_+zd zz{CmP*-JxYRJs{q(#NmenICVFx64>tThs4i>sxGM;+QP5@CvV{Wm&`GXJ(U&icWqV z=>Egn5-xiBbDXpP@PSI8Lv#4(t4Mn_HCf)+U{}J1Z7we_7%x%0-qmTr+=rc~=D|MD z=WIsI`V4V;(7yZ2k{Q0XsKduaEiNK3raac`*ISfAl{2iWp-oHb3*7?pcT5 zk9nRjVrQNLzn3}HQS;o&A3rfH6gs8en~IDG=n9c1hN#($E4<1?l?$!(;A3_mRS#@p z-Zw(0iyW>;3>l+hI(FdKVKe?=|Pc!&km;jJX~upkS8pIgUzPAk%m|2YX!Ffx8N{8kEQ=Oadh z1Uo2suop8P%}y3^R6X=2wDEi%LZA}873w~CKuY}qktq4DcezG0e(y}lAHY^hRhsH^=Nn(s;4h88%EqWB)FoFSFlH@rGP=5I*lD&K zH+PK3+VLmH34v3=>@$cvbl=ItLfi$eg@#v3*rCPZ5FmqR^Y2LS!0mJawXk!^=o56N zpqWL;c}V18pXM1R<(x!1jbF(*G;UE1K}u(Ee1^Yq`C0SK_DWs({M?kq0fRI`^-$~X z_c!A{w0p|^0XxUti_tUk+J;~@#}Jt3&*!Z>+Bq*PsOpWI!|ogoNj?0Li z)~XI7m=&Y*wyIkG>1zLayJi;&O6#2wn8rib6QZQ}dFf)dTRC<^YFy10C%Lz?Gf6lk z4KJ#)gKV!hosb7KN--IL}rcKxbK>!NCd;XgfaS8dr)>FbZ;ZH3nxBcJPq7pPa zfs6A0)|3ARIsexW5%b@$J#nC^E*yABKnVOl1gGY2{}9Mk=~4(Ikt|_UsI7ED`8U!3 zjw~TV{U>1uKfLmf0B-ER?uzT3>FEEI{S}lq@=x-w=>J4O2Qb1Y-xSGU%<~ZfL2^Z^ zZep|oi@s2|%O^Hc6bXtk)0*7m65xTv?P%9|CHU;E%VIEu~VQGNz9!i8d+I2v>Zf73Pg_Uu~c&R~7V1+BuU^ zQI5jD!4H!l`TkZF_+8M9Cttp3q#HNPP`y|ib6Xu?CMYtCC&47KVRAhybqhczPy+g? zH8rVyI+^xB67pZET+nhepyvPENGOq02iDhMKn`Ou(SV;lU~P7thHMPvCM6Dt3TZ$_ z?(hOUey07)`{1UA)p^v$>U z6V|Hv7fMvpa^*`$Tey}#sA2R8V91iRkJcA&Ti$I9wq}9>%q>Ytq;)MVEp#YRv`vz8 z#=>rijg{53fKm$6zBrIpFufTCx0v$%cP-ep9jZoyxsO(Vo1zPdir0TR_{Ic8izw;R z`?X=>C^<6OyYCYelIs|CJQV}eP$offE9uii2=@;;VxM&h{0OEmuc@y`Cs{zw zmZ)5VIVA9Jp+c}FAs;Gi5BU1de|oq|(7H5|9&O{>YpjL0H8(%BLaUp_QX&oO9NWVS ztGqXX^k+qVNnouIBSXUxMYMXBdwapBrWfCDZck%IOT-^(q%2HNPkZ*sj(fBMGv7Zw z+#Cfp+zq8EC{TEGuuS+w<2Ex4V3Vd=+9c#s!Q8#mZl3>YB5e7U;7_f;|M+q5&DRQA zg24;C{rtp1lD45wuP;(1YS=`r+*pA2dW1NP!8Al)7V%m^xEsTHza@iMhg@tN+F4u& b&(-#$pqaF$DJg{SaR5DSBdvED&T;<(Y3%x{ literal 692 zcmV;l0!#ggP)6e`|w02cRn9I2lPGWk^^SQ8xdGG%!eOLiRgg+>u=MGFaH6_?fl3AiLC*mVCDt~ zIGjmdJL9Ik{)nDzuSPfi0=f0!dw~PjC%Ijy`$6a-Bbz#zV4yMI5jDepI}2u7^czDp zFxso))#MgJ&A^}9faCp45TN5i#DK|JkFiEr7VmFde5IwV@c>cr5UsU`<|qb74VRV) zLURUgCJ3Tu4YZXeM;U5M4Gt6V)agrepA{%;ZK@1*&~z0vr6x%l9$XW!b0{GeC3v&z z!sR6f3L9+pB^+~Ht0%#4L29=Xb>1G0`o8}3t2fb=un*qV4SaJXECdC7Ig;dP!mG=E zXu2+Vq8Tv&nO>HOoz2HX4ZpeuudE38+0mXqvk-vSyDh7py@X(I1e6=GgdIPxiHLSmDXl5IhKkQgNS8g!~12q-!kaRj7PbQO?N~L0zN+mLVej<} Date: Wed, 10 Jul 2024 15:00:21 -0700 Subject: [PATCH 11/11] Update version to 0.11 --- build/azure-pipelines.yml | 2 +- build/scripts/CreateBuildInfo.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/azure-pipelines.yml b/build/azure-pipelines.yml index 562ba838..4c3ddc16 100644 --- a/build/azure-pipelines.yml +++ b/build/azure-pipelines.yml @@ -20,7 +20,7 @@ parameters: - release variables: - MSIXVersion: '0.1000' + MSIXVersion: '0.1100' solution: '**/DevHomeAzureExtension.sln' appxPackageDir: 'AppxPackages' testOutputArtifactDir: 'TestResults' diff --git a/build/scripts/CreateBuildInfo.ps1 b/build/scripts/CreateBuildInfo.ps1 index 65dfcc7a..17a84fc2 100644 --- a/build/scripts/CreateBuildInfo.ps1 +++ b/build/scripts/CreateBuildInfo.ps1 @@ -5,7 +5,7 @@ Param( ) $Major = "0" -$Minor = "10" +$Minor = "11" $Patch = "99" # default to 99 for local builds $versionSplit = $Version.Split(".");