Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/GitHub.App/Services/RepositoryCloneService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,20 @@ public async Task CloneRepository(
try
{
await vsGitServices.Clone(cloneUrl, repositoryPath, true, progress);

await usageTracker.IncrementCounter(x => x.NumberOfClones);

var repositoryUrl = new UriString(cloneUrl).ToRepositoryUrl();
var isDotCom = HostAddress.IsGitHubDotComUri(repositoryUrl);
if (isDotCom)
{
await usageTracker.IncrementCounter(x => x.NumberOfGitHubClones);
}
else
{
// If it isn't a GitHub URL, assume it's an Enterprise URL
await usageTracker.IncrementCounter(x => x.NumberOfEnterpriseClones);
}
}
catch (Exception ex)
{
Expand Down
33 changes: 33 additions & 0 deletions src/GitHub.App/ViewModels/Dialog/Clone/RepositoryCloneViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public class RepositoryCloneViewModel : ViewModelBase, IRepositoryCloneViewModel
readonly IOperatingSystem os;
readonly IConnectionManager connectionManager;
readonly IRepositoryCloneService service;
readonly IUsageService usageService;
readonly IUsageTracker usageTracker;
readonly IReadOnlyList<IRepositoryCloneTabViewModel> tabs;
string path;
IRepositoryModel previousRepository;
Expand All @@ -34,13 +36,17 @@ public RepositoryCloneViewModel(
IOperatingSystem os,
IConnectionManager connectionManager,
IRepositoryCloneService service,
IUsageService usageService,
IUsageTracker usageTracker,
IRepositorySelectViewModel gitHubTab,
IRepositorySelectViewModel enterpriseTab,
IRepositoryUrlViewModel urlTab)
{
this.os = os;
this.connectionManager = connectionManager;
this.service = service;
this.usageService = usageService;
this.usageTracker = usageTracker;

GitHubTab = gitHubTab;
EnterpriseTab = enterpriseTab;
Expand Down Expand Up @@ -119,6 +125,33 @@ public async Task InitializeAsync(IConnection connection)
}

this.WhenAnyValue(x => x.SelectedTabIndex).Subscribe(x => tabs[x].Activate().Forget());

// Users in group A will see the URL tab by default
if (await IsGroupA().ConfigureAwait(false))
{
SelectedTabIndex = 2;
}

switch (SelectedTabIndex)
{
case 0:
usageTracker.IncrementCounter(model => model.NumberOfCloneViewGitHubTab).Forget();
break;
case 1:
usageTracker.IncrementCounter(model => model.NumberOfCloneViewEnterpriseTab).Forget();
break;
case 2:
usageTracker.IncrementCounter(model => model.NumberOfCloneViewUrlTab).Forget();
break;
}
}

// Put 50% of users in group A
async Task<bool> IsGroupA()
{
var userGuid = await usageService.GetUserGuid().ConfigureAwait(false);
var lastByte = userGuid.ToByteArray().Last();
return lastByte % 2 == 0;
}

void BrowseForDirectory()
Expand Down
5 changes: 5 additions & 0 deletions src/GitHub.Exports/Models/UsageModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ public class MeasuresModel
public int ExecuteToggleInlineCommentMarginCommand { get; set; }
public int NumberOfPullRequestFileMarginToggleInlineCommentMargin { get; set; }
public int NumberOfPullRequestFileMarginViewChanges { get; set; }
public int NumberOfCloneViewGitHubTab { get; set; }
public int NumberOfCloneViewEnterpriseTab { get; set; }
public int NumberOfCloneViewUrlTab { get; set; }
public int NumberOfGitHubClones { get; set; }
public int NumberOfEnterpriseClones { get; set; }
}
}
}
15 changes: 10 additions & 5 deletions test/GitHub.App.UnitTests/Services/RepositoryCloneServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,13 @@ public async Task ClonesToRepositoryPathAsync()
await vsGitServices.Received().Clone("https://github.com/foo/bar", @"c:\dev\bar", true);
}

[Test]
public async Task UpdatesMetricsWhenRepositoryClonedAsync()
[TestCase("https://github.com/foo/bar", 1, nameof(UsageModel.MeasuresModel.NumberOfClones))]
[TestCase("https://github.com/foo/bar", 1, nameof(UsageModel.MeasuresModel.NumberOfGitHubClones))]
[TestCase("https://github.com/foo/bar", 0, nameof(UsageModel.MeasuresModel.NumberOfEnterpriseClones))]
[TestCase("https://enterprise.com/foo/bar", 1, nameof(UsageModel.MeasuresModel.NumberOfClones))]
[TestCase("https://enterprise.com/foo/bar", 1, nameof(UsageModel.MeasuresModel.NumberOfEnterpriseClones))]
[TestCase("https://enterprise.com/foo/bar", 0, nameof(UsageModel.MeasuresModel.NumberOfGitHubClones))]
public async Task UpdatesMetricsWhenRepositoryClonedAsync(string cloneUrl, int numberOfCalls, string counterName)
{
var serviceProvider = Substitutes.ServiceProvider;
var operatingSystem = serviceProvider.GetOperatingSystem();
Expand All @@ -37,12 +42,12 @@ public async Task UpdatesMetricsWhenRepositoryClonedAsync()
var usageTracker = Substitute.For<IUsageTracker>();
var cloneService = new RepositoryCloneService(operatingSystem, vsGitServices, graphqlFactory, usageTracker);

await cloneService.CloneRepository("https://github.com/foo/bar", @"c:\dev\bar");
await cloneService.CloneRepository(cloneUrl, @"c:\dev\bar");
var model = UsageModel.Create(Guid.NewGuid());

await usageTracker.Received().IncrementCounter(
await usageTracker.Received(numberOfCalls).IncrementCounter(
Arg.Is<Expression<Func<UsageModel.MeasuresModel, int>>>(x =>
((MemberExpression)x.Body).Member.Name == nameof(model.Measures.NumberOfClones)));
((MemberExpression)x.Body).Member.Name == counterName));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using System;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Numerics;
using System.Threading.Tasks;
using GitHub.Extensions;
using GitHub.Models;
Expand All @@ -26,6 +29,42 @@ public async Task GitHubPage_Is_Initialized()
target.EnterpriseTab.DidNotReceiveWithAnyArgs().Initialize(null);
}

[TestCase("https://github.com", false, 0)]
[TestCase("https://enterprise.com", false, 1)]
[TestCase("https://github.com", true, 2, Description = "Show URL tab for GitHub connections")]
[TestCase("https://enterprise.com", true, 2, Description = "Show URL tab for Enterprise connections")]
public async Task Default_SelectedTabIndex_For_Group(string address, bool isGroupA, int expectTabIndex)
{
var cm = CreateConnectionManager(address);
var connection = cm.Connections[0];
var usageService = CreateUsageService(isGroupA);
var target = CreateTarget(connectionManager: cm, usageService: usageService);

await target.InitializeAsync(connection);

Assert.That(target.SelectedTabIndex, Is.EqualTo(expectTabIndex));
}

[TestCase("https://github.com", false, 1, nameof(UsageModel.MeasuresModel.NumberOfCloneViewGitHubTab))]
[TestCase("https://enterprise.com", false, 1, nameof(UsageModel.MeasuresModel.NumberOfCloneViewEnterpriseTab))]
[TestCase("https://github.com", true, 1, nameof(UsageModel.MeasuresModel.NumberOfCloneViewUrlTab))]
[TestCase("https://enterprise.com", true, 1, nameof(UsageModel.MeasuresModel.NumberOfCloneViewUrlTab))]
public async Task IncrementCounter_Showing_Default_Tab(string address, bool isGroupA, int numberOfCalls, string counterName)
{
var cm = CreateConnectionManager(address);
var connection = cm.Connections[0];
var usageService = CreateUsageService(isGroupA);
var usageTracker = Substitute.For<IUsageTracker>();
var target = CreateTarget(connectionManager: cm, usageService: usageService, usageTracker: usageTracker);
usageTracker.IncrementCounter(null).ReturnsForAnyArgs(Task.CompletedTask);

await target.InitializeAsync(connection).ConfigureAwait(false);

await usageTracker.Received(numberOfCalls).IncrementCounter(
Arg.Is<Expression<Func<UsageModel.MeasuresModel, int>>>(x =>
((MemberExpression)x.Body).Member.Name == counterName));
}

[Test]
public async Task EnterprisePage_Is_Initialized()
{
Expand Down Expand Up @@ -274,6 +313,8 @@ static RepositoryCloneViewModel CreateTarget(
IOperatingSystem os = null,
IConnectionManager connectionManager = null,
IRepositoryCloneService service = null,
IUsageService usageService = null,
IUsageTracker usageTracker = null,
IRepositorySelectViewModel gitHubTab = null,
IRepositorySelectViewModel enterpriseTab = null,
IRepositoryUrlViewModel urlTab = null,
Expand All @@ -282,6 +323,8 @@ static RepositoryCloneViewModel CreateTarget(
os = os ?? Substitute.For<IOperatingSystem>();
connectionManager = connectionManager ?? CreateConnectionManager("https://github.com");
service = service ?? CreateRepositoryCloneService(defaultClonePath);
usageService = usageService ?? CreateUsageService();
usageTracker = usageTracker ?? Substitute.For<IUsageTracker>();
gitHubTab = gitHubTab ?? CreateSelectViewModel();
enterpriseTab = enterpriseTab ?? CreateSelectViewModel();
urlTab = urlTab ?? Substitute.For<IRepositoryUrlViewModel>();
Expand All @@ -290,11 +333,23 @@ static RepositoryCloneViewModel CreateTarget(
os,
connectionManager,
service,
usageService,
usageTracker,
gitHubTab,
enterpriseTab,
urlTab);
}

static IUsageService CreateUsageService(bool isGroupA = false)
{
var usageService = Substitute.For<IUsageService>();
var guidBytes = new byte[16];
guidBytes[guidBytes.Length - 1] = (byte)(isGroupA ? 0 : 1);
var userGuid = new Guid(guidBytes);
usageService.GetUserGuid().Returns(userGuid);
return usageService;
}

static IRepositoryModel CreateRepositoryModel(string repo = "owner/repo")
{
var split = repo.Split('/');
Expand Down