Skip to content

Commit

Permalink
Merge branch 'main' into user/dhoehna/MovingTwoMissedPRs
Browse files Browse the repository at this point in the history
  • Loading branch information
dhoehna committed Nov 10, 2023
2 parents 4e6b9cf + 5b7bc3a commit 35e9ab9
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 33 deletions.
Binary file added src/AzureExtension/Assets/AzureExtensionDark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 23 additions & 9 deletions src/AzureExtension/Client/AzureUri.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ private AzureHostType InitializeAzureHostType()
{
return AzureHostType.NotHosted;
}
else if (Uri.Host.Equals("dev.azure.com", StringComparison.OrdinalIgnoreCase))
else if (Uri.Host.EndsWith("dev.azure.com", StringComparison.OrdinalIgnoreCase))
{
return AzureHostType.Modern;
}
Expand Down Expand Up @@ -211,15 +211,26 @@ private string InitializeOrganization()

try
{
return HostType switch
switch (HostType)
{
// https://dev.azure.com/{organization} (modern)
AzureHostType.Modern => Uri.Segments[1].Replace("/", string.Empty),

// https://{organization}.visualstudio.com (legacy)
AzureHostType.Legacy => Uri.Host.Replace(".visualstudio.com", string.Empty, StringComparison.OrdinalIgnoreCase),
_ => string.Empty,
};
case AzureHostType.Modern:
return Uri.Segments[1].Replace("/", string.Empty);

case AzureHostType.Legacy:
// Legacy format can have "vssps" in the uri, which we need to ignore for
// extracting the url
if (Uri.Host.EndsWith(".vssps.visualstudio.com", StringComparison.OrdinalIgnoreCase))
{
return Uri.Host.Replace(".vssps.visualstudio.com", string.Empty, StringComparison.OrdinalIgnoreCase);
}
else
{
return Uri.Host.Replace(".visualstudio.com", string.Empty, StringComparison.OrdinalIgnoreCase);
}

default:
return string.Empty;
}
}
catch (Exception e)
{
Expand Down Expand Up @@ -423,6 +434,9 @@ private Uri InitializeConnection()
case AzureHostType.Legacy:

// Legacy format is just the authority, as the organization is in the subdomain.
// Note that Authority will not contain the port number unless the port
// number differs from the default port. So if Port 443 is specified, Authority will
// not list it as that is the default port for the https scheme.
var legacyUriString = Uri.Scheme + "://" + Uri.Authority;
legacyUriString = legacyUriString.TrimEnd('/') + '/';
if (!Uri.TryCreate(legacyUriString, UriKind.Absolute, out newUri))
Expand Down
2 changes: 1 addition & 1 deletion src/AzureExtension/Providers/RepositoryProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public RepositoryProvider(IRandomAccessStreamReference icon)

public RepositoryProvider()
{
Icon = RandomAccessStreamReference.CreateFromUri(new Uri("https://www.GitHub.com/microsoft/devhome"));
Icon = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///AzureExtension/Assets/AzureExtensionDark.png"));
}

public IAsyncOperation<RepositoryUriSupportResult> IsUriSupportedAsync(Uri uri)
Expand Down
2 changes: 1 addition & 1 deletion src/AzureExtension/Strings/en-US/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@
<comment>The name of our application</comment>
</data>
<data name="RepositoryProviderDisplayName" xml:space="preserve">
<value>Azure</value>
<value>Azure DevOps</value>
<comment>Shown in various places, is Azure even localized as a product name?</comment>
</data>
<data name="Widget_Template_DaysAgo" xml:space="preserve">
Expand Down
132 changes: 110 additions & 22 deletions test/AzureExtension/Widgets/Validation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public enum AzureUriType
Query,
Repository,
Unknown,
SignIn,
Garbage, // Anything that is expected to fail creation by a normal Uri.
}

Expand Down Expand Up @@ -92,6 +93,43 @@ public void AzureUriValidation()
Tuple.Create("https://organization.visualstudio.com/collection/project/_git/repository", AzureUriType.Repository, true),
Tuple.Create("https://organization.visualstudio.com/collection/project/_git/repository/", AzureUriType.Repository, true),
Tuple.Create("https://organization.visualstudio.com/collection/project/_git/repository/some/other/stuff", AzureUriType.Repository, true),

// Azure DevOps services SignIn Uris
Tuple.Create("https://app.vssps.visualstudio.com/", AzureUriType.SignIn, true),
Tuple.Create("https://app.vssps.visualstudio.com/with/extra/stuff", AzureUriType.SignIn, true),
Tuple.Create("https://app.vssps.visualstudio.com:443/", AzureUriType.SignIn, true),
Tuple.Create("https://app.vssps.visualstudio.com:443/with/extra/stuff", AzureUriType.SignIn, true),
Tuple.Create("https://organization.vssps.visualstudio.com/", AzureUriType.SignIn, true),
Tuple.Create("https://organization.vssps.visualstudio.com/with/extra/stuff", AzureUriType.SignIn, true),
Tuple.Create("https://organization.vssps.visualstudio.com:443/", AzureUriType.SignIn, true),
Tuple.Create("https://organization.vssps.visualstudio.com:443/with/extra/stuff", AzureUriType.SignIn, true),

// Azure DevOps services SignIn Uris, modern format.
// Note port 443 is the default port for https, so Uri objects remove it.
Tuple.Create("https://vssps.dev.azure.com/organization", AzureUriType.SignIn, false),
Tuple.Create("https://vssps.dev.azure.com/organization/", AzureUriType.SignIn, false),
Tuple.Create("https://vssps.dev.azure.com:443/organization", AzureUriType.SignIn, false),
Tuple.Create("https://vssps.dev.azure.com:443/organization/", AzureUriType.SignIn, false),
Tuple.Create("https://vssps.dev.azure.com/organization/with/extra/stuff", AzureUriType.SignIn, false),
Tuple.Create("https://vssps.dev.azure.com/organization/with/extra/stuff/", AzureUriType.SignIn, false),
Tuple.Create("https://vssps.dev.azure.com:443/organization/with/extra/stuff", AzureUriType.SignIn, false),
Tuple.Create("https://vssps.dev.azure.com:443/organization/with/extra/stuff/", AzureUriType.SignIn, false),

// Azure Devops SignIn Uris, legacy format.
// Note port 443 is the default port for https, so Uri objects remove it.
Tuple.Create("https://app.vssps.visualstudio.com/", AzureUriType.SignIn, false),
Tuple.Create("https://app.vssps.visualstudio.com:443/", AzureUriType.SignIn, false),
Tuple.Create("https://app.vssps.visualstudio.com:443/with/extra/stuff", AzureUriType.SignIn, false),
Tuple.Create("https://organization.vssps.visualstudio.com/", AzureUriType.SignIn, false),
Tuple.Create("https://organization.vssps.visualstudio.com/with/extra/stuff", AzureUriType.SignIn, false),
Tuple.Create("https://organization.vssps.visualstudio.com:443/", AzureUriType.SignIn, false),
Tuple.Create("https://organization.vssps.visualstudio.com:443/with/extra/stuff", AzureUriType.SignIn, false),
Tuple.Create("https://app.vssps.visualstudio.com:443/", AzureUriType.SignIn, false),
Tuple.Create("https://app.vssps.visualstudio.com:443/with/extra/stuff", AzureUriType.SignIn, false),
Tuple.Create("https://organization.vssps.visualstudio.com/", AzureUriType.SignIn, false),
Tuple.Create("https://organization.vssps.visualstudio.com/with/extra/stuff", AzureUriType.SignIn, false),
Tuple.Create("https://organization.vssps.visualstudio.com:443/", AzureUriType.SignIn, false),
Tuple.Create("https://organization.vssps.visualstudio.com:443/with/extra/stuff", AzureUriType.SignIn, false),
};

var testUrisInvalid = new List<Tuple<string, AzureUriType, bool>>
Expand Down Expand Up @@ -122,14 +160,21 @@ public void AzureUriValidation()
Assert.AreEqual(azureUri.Uri, uri);
}

// All Valid inputs should be valid.
Assert.IsTrue(azureUri.IsValid);

// We only support hosted for now.
Assert.IsTrue(azureUri.IsHosted);
var org = azureUri.Organization;
var project = azureUri.Project;
var repository = azureUri.Repository;
var query = azureUri.Query;
TestContext?.WriteLine($"Org: {org} Project: {project} Repository: {repository} Query: {query}");
Assert.AreEqual("organization", org);
Assert.AreEqual("project", project);
TestContext?.WriteLine($"Org: {azureUri.Organization} Project: {azureUri.Project} Repository: {azureUri.Repository} Query: {azureUri.Query}");

if (uriTuple.Item2 != AzureUriType.SignIn)
{
// Signin Uris may not have project.
// Signin Uris can also have "app" as the organization.
// Organization will be validated as part of that type.
Assert.AreEqual("organization", azureUri.Organization);
Assert.AreEqual("project", azureUri.Project);
}

// Validate the constructor is identical if it is created from a Uri instead of a string.
if (uriTuple.Item2 != AzureUriType.Garbage)
Expand All @@ -139,44 +184,87 @@ public void AzureUriValidation()
Assert.AreEqual(azureUri.ToString(), uri.ToString());
Assert.AreEqual(azureUri.OriginalString, uri.OriginalString);
Assert.AreEqual(azureUri.Uri, uri);

var connectionUri = azureUri.Connection;
Assert.IsNotNull(connectionUri);
TestContext?.WriteLine($"Connection: {connectionUri}");

// All Https Uris have port 443 by default.
// We require https uri, therefore we must have port 443.
Assert.IsTrue(connectionUri.Port == 443);

// A properly constructed connectionUri will be contained within the full Uri.
// We use a separate standard Uri to compare so we get expected Uri behavior w/r/t
// the Authority, which will not contain the port if it is the scheme's default.

// The only modification is to enforce a trailing / which may or may not be there,
// but the trailing slash is part of the Authority and we normalize to that.
// We are testing for functional equivalence.
var normalizedOriginal = uri.ToString().Trim('/') + '/';
Assert.IsTrue(normalizedOriginal.StartsWith(connectionUri.ToString(), StringComparison.OrdinalIgnoreCase));
}

// Verify Query Uris have expected values.
if (uriTuple.Item2 == AzureUriType.Query)
{
Assert.IsTrue(azureUri.IsQuery);
Assert.AreEqual("12345678-1234-1234-1234-1234567890ab", query);
Assert.AreEqual("12345678-1234-1234-1234-1234567890ab", azureUri.Query);
}
else
{
Assert.IsFalse(azureUri.IsQuery);
Assert.AreEqual(string.Empty, query);
Assert.AreEqual(string.Empty, azureUri.Query);
}

// Verify repository Uris have expected values.
if (uriTuple.Item2 == AzureUriType.Repository)
{
Assert.IsTrue(azureUri.IsRepository);
Assert.AreEqual("repository", repository);
Assert.AreEqual("repository", azureUri.Repository);
}
else
{
Assert.IsFalse(azureUri.IsRepository);
Assert.AreEqual(string.Empty, repository);
Assert.AreEqual(string.Empty, azureUri.Repository);
}

var connectionUri = azureUri.Connection;
Assert.IsNotNull(connectionUri);
TestContext?.WriteLine($"Connection: {connectionUri}");

// Verify connection URI remains in the original format. Legacy URIs should use legacy connections.
if (azureUri.HostType == AzureHostType.Legacy)
if (uriTuple.Item2 == AzureUriType.SignIn)
{
Assert.AreEqual("https://organization.visualstudio.com/", connectionUri.ToString());
}
else
{
Assert.AreEqual("https://dev.azure.com/organization/", connectionUri.ToString());
if (azureUri.Organization.Equals("app", StringComparison.OrdinalIgnoreCase))
{
// App is valid if it is parsed as an organization, but only
// on legacy Uris. This directly validates that
// app.vssps.visualstudio.com is a valid input uri.
Assert.IsTrue(azureUri.HostType == AzureHostType.Legacy);
}
else
{
// All other matches are assumed to be organization names.
// This ensures we do not treat the "vssps" part as being part of the org name.
Assert.AreEqual("organization", azureUri.Organization);
}

// Test for equivalence of Connection Uris when the base Uri and the Connection
// should be equivalent.
if (azureUri.HostType == AzureHostType.Modern)
{
// Modern hosts have bare minimum Org requirement.
if (azureUri.Uri.Segments.Length == 2)
{
// Exact equivalence may not be true depending on trailing slash in the
// original Uri, but these are still functionally equivalent.
var normalizedOriginal = azureUri.Uri.ToString().Trim('/') + '/';
Assert.IsTrue(normalizedOriginal.Equals(azureUri.Connection.ToString(), StringComparison.OrdinalIgnoreCase));
}
}
else
{
if (azureUri.Uri.Segments.Length < 2)
{
var normalizedOriginal = azureUri.Uri.ToString().Trim('/') + '/';
Assert.IsTrue(normalizedOriginal.Equals(azureUri.Connection.ToString(), StringComparison.OrdinalIgnoreCase));
}
}
}

TestContext?.WriteLine($"Valid: {uriTuple.Item1}");
Expand Down

0 comments on commit 35e9ab9

Please sign in to comment.