Skip to content

Commit

Permalink
Create GetGitTrendsStatistics API (#241)
Browse files Browse the repository at this point in the history
* Create GetGitTrendsStatistics

* Update GitTrendsStatisticsService to use Azure Functions Backend

* Fix GitTrendsStatisticsServiceTests
  • Loading branch information
TheCodeTraveler authored Mar 8, 2021
1 parent b9a3317 commit 26014eb
Show file tree
Hide file tree
Showing 15 changed files with 124 additions and 47 deletions.
19 changes: 19 additions & 0 deletions GitTrends.Functions/Functions/GetGitTrendsStatistics.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Threading.Tasks;
using GitTrends.Shared;
using Microsoft.AspNetCore.Http;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;

namespace GitTrends.Functions
{
class GetGitTrendsStatistics
{
readonly BlobStorageService _blobStorageService;

public GetGitTrendsStatistics(BlobStorageService blobStorageService) => _blobStorageService = blobStorageService;

[FunctionName(nameof(GetGitTrendsStatistics))]
public Task<GitTrendsStatisticsDTO> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequest request, ILogger log) => _blobStorageService.GetGitTrendsStatistics();
}
}
3 changes: 1 addition & 2 deletions GitTrends.Functions/Functions/GetLibraries.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Threading.Tasks;
using GitTrends.Shared;
using Microsoft.AspNetCore.Http;
Expand Down
43 changes: 43 additions & 0 deletions GitTrends.Functions/Functions/UpdateGitTrendsStatistics.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using GitTrends.Shared;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;

namespace GitTrends.Functions
{
class UpdateGitTrendsStatistics
{
readonly GitHubApiV3Service _gitHubApiV3Service;
readonly BlobStorageService _blobStorageService;

public UpdateGitTrendsStatistics(GitHubApiV3Service gitHubApiV3Service, BlobStorageService blobStorageService) =>
(_gitHubApiV3Service, _blobStorageService) = (gitHubApiV3Service, blobStorageService);

[FunctionName(nameof(UpdateGitTrendsStatistics))]
public async Task Run([TimerTrigger("0 0 0 * * *")] TimerInfo myTimer, ILogger log)
{
var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(2));

var getRepositoryTask = _gitHubApiV3Service.GetRepository(GitHubConstants.GitTrendsRepoOwner, GitHubConstants.GitTrendsRepoName, cancellationTokenSource.Token);
var getGitTrendsContributorsTask = _gitHubApiV3Service.GetGitTrendsContributors(cancellationTokenSource.Token);

await Task.WhenAll(getRepositoryTask, getGitTrendsContributorsTask).ConfigureAwait(false);

var repository = await getRepositoryTask.ConfigureAwait(false);
var contributors = await getGitTrendsContributorsTask.ConfigureAwait(false);

var statistics = new GitTrendsStatisticsDTO(new Uri(repository.Url ?? throw new NullReferenceException()),
repository.StarCount ?? throw new NullReferenceException(),
repository.WatchersCount,
repository.ForkCount,
contributors);

var blobName = $"Statistics_{DateTime.UtcNow:o}.json";
log.LogInformation($"Saving Statistics to Blob Storage: {blobName}");

await _blobStorageService.UploadStatistics(statistics, blobName).ConfigureAwait(false);
}
}
}
34 changes: 22 additions & 12 deletions GitTrends.Functions/Services/BlobStorageService.cs
Original file line number Diff line number Diff line change
@@ -1,41 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Collections.Generic;
using GitTrends.Shared;
using Microsoft.WindowsAzure.Storage.Blob;
using Newtonsoft.Json;
using GitTrends.Shared;

namespace GitTrends.Functions
{
class BlobStorageService
{
const string _libraryContainerName = "librarycache";
const string _gitTrendsStatisticsContainerName = "gittrendsstatistics";

readonly CloudBlobClient _blobClient;

public BlobStorageService(CloudBlobClient cloudBlobClient) => _blobClient = cloudBlobClient;

public Task UploadNuGetLibraries(IEnumerable<NuGetPackageModel> nuGetPackageModels, string blobName)
public Task UploadNuGetLibraries(IEnumerable<NuGetPackageModel> nuGetPackageModels, string blobName) => UploadValue(nuGetPackageModels, blobName, _libraryContainerName);

public Task UploadStatistics(GitTrendsStatisticsDTO gitTrendsStatistics, string blobName) => UploadValue(gitTrendsStatistics, blobName, _gitTrendsStatisticsContainerName);

public Task<GitTrendsStatisticsDTO> GetGitTrendsStatistics() => GetLatestValue<GitTrendsStatisticsDTO>(_gitTrendsStatisticsContainerName);

public Task<IReadOnlyList<NuGetPackageModel>> GetNuGetLibraries() => GetLatestValue<IReadOnlyList<NuGetPackageModel>>(_libraryContainerName);

async Task UploadValue<T>(T data, string blobName, string containerName)
{
var container = GetBlobContainer(_libraryContainerName);
container.CreateIfNotExistsAsync().ConfigureAwait(false);
var container = GetBlobContainer(containerName);
await container.CreateIfNotExistsAsync().ConfigureAwait(false);

var blob = container.GetBlockBlobReference(blobName);
return blob.UploadTextAsync(JsonConvert.SerializeObject(nuGetPackageModels));
await blob.UploadTextAsync(JsonConvert.SerializeObject(data)).ConfigureAwait(false);
}

public async Task<IReadOnlyList<NuGetPackageModel>> GetNuGetLibraries()
{
async Task<T> GetLatestValue<T>(string containerName)
{
var blobList = new List<CloudBlockBlob>();
await foreach (var blob in GetBlobs<CloudBlockBlob>(_libraryContainerName).ConfigureAwait(false))
await foreach (var blob in GetBlobs<CloudBlockBlob>(containerName).ConfigureAwait(false))
{
blobList.Add(blob);
}

var nugetPackageModelBlob = blobList.OrderByDescending(x => x.Properties.Created).First();
var serializedNuGetPackageList = await nugetPackageModelBlob.DownloadTextAsync().ConfigureAwait(false);
var newestBlob = blobList.OrderByDescending(x => x.Properties.Created).First();
var serializedBlobContents = await newestBlob.DownloadTextAsync().ConfigureAwait(false);

return JsonConvert.DeserializeObject<IReadOnlyList<NuGetPackageModel>>(serializedNuGetPackageList) ?? throw new NullReferenceException();
return JsonConvert.DeserializeObject<T>(serializedBlobContents) ?? throw new NullReferenceException();
}

async IAsyncEnumerable<T> GetBlobs<T>(string containerName, string prefix = "", int? maxresultsPerQuery = null, BlobListingDetails blobListingDetails = BlobListingDetails.None) where T : ICloudBlob
Expand Down
11 changes: 9 additions & 2 deletions GitTrends.Functions/Services/GitHubApiV3Service.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Threading;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using GitHubApiStatus;
using GitTrends.Shared;
Expand All @@ -12,12 +13,18 @@ class GitHubApiV3Service : BaseGitHubApiService

public GitHubApiV3Service(IGitHubApiV3 gitHubApiV3Client,
ILogger<GitHubApiV3Service> logger,
IGitHubApiStatusService gitHubApiStatusService) : base(gitHubApiStatusService,logger)
IGitHubApiStatusService gitHubApiStatusService) : base(gitHubApiStatusService, logger)
{
_gitHubApiV3Client = gitHubApiV3Client;
}

public Task<RepositoryFile> GetGitTrendsFile(string fileName, CancellationToken cancellationToken) =>
AttemptAndRetry_Functions(() => _gitHubApiV3Client.GetGitTrendsFile(fileName), cancellationToken);

public Task<IReadOnlyList<Contributor>> GetGitTrendsContributors(CancellationToken cancellationToken) =>
AttemptAndRetry_Functions(() => _gitHubApiV3Client.GetContributors(GitHubConstants.GitTrendsRepoOwner, GitHubConstants.GitTrendsRepoName), cancellationToken);

public Task<GetRepositoryResponse> GetRepository(string owner, string repositoryName, CancellationToken cancellationToken) =>
AttemptAndRetry_Functions(() => _gitHubApiV3Client.GetRepository(owner, repositoryName), cancellationToken);
}
}
1 change: 1 addition & 0 deletions GitTrends.Shared/GitTrends.Shared.projitems
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,6 @@
<Compile Include="$(MSBuildThisFileDirectory)Models\GetRepositoryResponse.cs" />
<Compile Include="$(MSBuildThisFileDirectory)StringExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)RefitExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\DTOs\GitTrendsStatisticsDTO.cs" />
</ItemGroup>
</Project>
3 changes: 3 additions & 0 deletions GitTrends.Shared/Interfaces/IAzureFunctionsApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,8 @@ public interface IAzureFunctionsApi

[Get("/GetLibraries")]
Task<IReadOnlyList<NuGetPackageModel>> GetLibraries();

[Get("/GetGitTrendsStatistics")]
Task<GitTrendsStatisticsDTO> GetGitTrendsStatistics();
}
}
7 changes: 7 additions & 0 deletions GitTrends.Shared/Models/DTOs/GitTrendsStatisticsDTO.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using System;
using System.Collections.Generic;

namespace GitTrends.Shared
{
public record GitTrendsStatisticsDTO(Uri GitHubUri, long Stars, long Watchers, long Forks, IReadOnlyList<Contributor> Contributors);
}
4 changes: 2 additions & 2 deletions GitTrends.UITests/Pages/SettingsPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,8 @@ Task SelectFromPicker(int rowNumber, int totalRows, string selection, Query pick
{
var rowOffset = App switch
{
iOSApp _ => rowNumber.Equals(totalRows - 1) ? -1 : 1,
AndroidApp _ => 0,
iOSApp => rowNumber.Equals(totalRows - 1) ? -1 : 1,
AndroidApp => 0,
_ => throw new NotSupportedException()
};

Expand Down
7 changes: 4 additions & 3 deletions GitTrends.UITests/Tests/Base/BaseUITest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ public virtual Task BeforeEachTest()
return UserType switch
{
UserType.Demo => SetupDemoUser(),
UserType.LoggedIn => SetupLoggedInUser(),
UserType.Neither => SetupNeither(),
UserType.LoggedIn => SetupLoggedInUser(),
_ => throw new NotSupportedException()
};
}
Expand Down Expand Up @@ -95,13 +95,14 @@ protected async Task LoginToGitHub()
{
var token = await AzureFunctionsApiService.GetTestToken().ConfigureAwait(false);

App.InvokeBackdoorMethod(BackdoorMethodConstants.SetGitHubUser, token.AccessToken);

GitHubToken? currentUserToken = null;

while (currentUserToken is null)
{
App.InvokeBackdoorMethod(BackdoorMethodConstants.SetGitHubUser, token.AccessToken);

await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);

currentUserToken = App.InvokeBackdoorMethod<GitHubToken?>(BackdoorMethodConstants.GetGitHubToken);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,19 @@ class GitTrendsStatisticsServiceTests : BaseTest
public async Task InitializeContributorsTest()
{
//Arrange
DateTimeOffset beforeTest, afterTest;
IReadOnlyList<Contributor> contributors_Initial, contributors_Final;
long? watchers_Initial, watchers_Final, stars_Initial, stars_Final, forks_Initial, forks_Final;

var gitTrendsStatisticsService = ServiceCollection.ServiceProvider.GetRequiredService<GitTrendsStatisticsService>();

//Act
beforeTest = DateTimeOffset.UtcNow;
stars_Initial = gitTrendsStatisticsService.Stars;
forks_Initial = gitTrendsStatisticsService.Forks;
watchers_Initial = gitTrendsStatisticsService.Watchers;
contributors_Initial = gitTrendsStatisticsService.Contributors;

await gitTrendsStatisticsService.Initialize(CancellationToken.None).ConfigureAwait(false);

afterTest = DateTimeOffset.UtcNow;
stars_Final = gitTrendsStatisticsService.Stars;
forks_Final = gitTrendsStatisticsService.Forks;
watchers_Final = gitTrendsStatisticsService.Watchers;
Expand Down Expand Up @@ -61,8 +58,6 @@ public async Task InitializeContributorsTest()
Assert.IsTrue(isAvatarUrlValid);
Assert.IsTrue(isGitHubUrlValid);
Assert.Greater(contributor.ContributionCount, 0);
Assert.Greater(afterTest, contributor.DataDownloadedAt);
Assert.Less(beforeTest, contributor.DataDownloadedAt);
Assert.IsFalse(string.IsNullOrEmpty(contributor.Login));
}
}
Expand Down
1 change: 1 addition & 0 deletions GitTrends/Services/AzureFunctionsApiService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public AzureFunctionsApiService(IAnalyticsService analyticsService, IMainThread
_azureFunctionsApiClient = azureFunctionsApi;
}

public Task<GitTrendsStatisticsDTO> GetGitTrendsStatistics(CancellationToken cancellationToken) => AttemptAndRetry_Mobile(() => _azureFunctionsApiClient.GetGitTrendsStatistics(), cancellationToken);
public Task<GetGitHubClientIdDTO> GetGitHubClientId(CancellationToken cancellationToken) => AttemptAndRetry_Mobile(() => _azureFunctionsApiClient.GetGitTrendsClientId(), cancellationToken);
public Task<GitHubToken> GenerateGitTrendsOAuthToken(GenerateTokenDTO generateTokenDTO, CancellationToken cancellationToken) => AttemptAndRetry_Mobile(() => _azureFunctionsApiClient.GenerateGitTrendsOAuthToken(generateTokenDTO), cancellationToken);
public Task<SyncFusionDTO> GetSyncfusionInformation(CancellationToken cancellationToken) => AttemptAndRetry_Mobile(() => _azureFunctionsApiClient.GetSyncfusionInformation(SyncfusionService.AssemblyVersionNumber), cancellationToken);
Expand Down
5 changes: 0 additions & 5 deletions GitTrends/Services/GitHubApiV3Service.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,6 @@ public GitHubApiV3Service(IMainThread mainThread,
_gitHubUserService = gitHubUserService;
}

public Task<GetRepositoryResponse> GetRepository(string owner, string repositoryName, CancellationToken cancellationToken) =>
AttemptAndRetry_Mobile(() => _githubApiClient.GetRepository(owner, repositoryName), cancellationToken);

public Task<IReadOnlyList<Contributor>> GetGitTrendsContributors(CancellationToken cancellationToken) => AttemptAndRetry_Mobile(() => _githubApiClient.GetContributors(GitHubConstants.GitTrendsRepoOwner, GitHubConstants.GitTrendsRepoName), cancellationToken);

public Task<RepositoryFile> GetGitTrendsFile(string fileName, CancellationToken cancellationToken) => AttemptAndRetry_Mobile(() => _githubApiClient.GetGitTrendsFile(fileName), cancellationToken);

public async Task<RepositoryViewsResponseModel> GetRepositoryViewStatistics(string owner, string repo, CancellationToken cancellationToken)
Expand Down
24 changes: 10 additions & 14 deletions GitTrends/Services/GitTrendsStatisticsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@ public class GitTrendsStatisticsService
{
readonly IPreferences _preferences;
readonly IAnalyticsService _analyticsService;
readonly GitHubApiV3Service _gitHubApiV3Service;
readonly ImageCachingService _imageCachingService;
readonly AzureFunctionsApiService _azureFunctionsApiService;

public GitTrendsStatisticsService(IPreferences preferences,
IAnalyticsService analyticsService,
GitHubApiV3Service gitHubApiV3Service,
ImageCachingService imageCachingService)
ImageCachingService imageCachingService,
AzureFunctionsApiService azureFunctionsApiService)
{
_preferences = preferences;
_analyticsService = analyticsService;
_gitHubApiV3Service = gitHubApiV3Service;
_imageCachingService = imageCachingService;
_azureFunctionsApiService = azureFunctionsApiService;
}

public Uri? GitHubUri
Expand Down Expand Up @@ -99,19 +99,15 @@ public async ValueTask Initialize(CancellationToken cancellationToken)

async Task initialize()
{
var getRepositoryTask = _gitHubApiV3Service.GetRepository(GitHubConstants.GitTrendsRepoOwner, GitHubConstants.GitTrendsRepoName, cancellationToken);
var getGitTrendsContributorsTask = _gitHubApiV3Service.GetGitTrendsContributors(cancellationToken);
var gittrendsStatistics = await _azureFunctionsApiService.GetGitTrendsStatistics(cancellationToken).ConfigureAwait(false);

await Task.WhenAll(getRepositoryTask, getGitTrendsContributorsTask).ConfigureAwait(false);
Stars = gittrendsStatistics.Stars;
Forks = gittrendsStatistics.Forks;
Watchers = gittrendsStatistics.Watchers;
GitHubUri = gittrendsStatistics.GitHubUri;

var gitTrendsRepository = await getRepositoryTask.ConfigureAwait(false);
Contributors = gittrendsStatistics.Contributors;

Stars = gitTrendsRepository?.StarCount;
Forks = gitTrendsRepository?.ForkCount;
Watchers = gitTrendsRepository?.WatchersCount;
GitHubUri = gitTrendsRepository?.Url is null ? null : new Uri(gitTrendsRepository?.Url);

Contributors = await getGitTrendsContributorsTask.ConfigureAwait(false);
foreach (var contributor in Contributors)
_imageCachingService.PreloadImage(contributor.AvatarUrl).SafeFireAndForget(ex => _analyticsService.Report(ex));
}
Expand Down
4 changes: 2 additions & 2 deletions GitTrends/Services/MediaElementService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ public MediaElementService(IPreferences preferences,
IAnalyticsService analyticsService,
AzureFunctionsApiService azureFunctionsApiService)
{
_azureFunctionsApiService = azureFunctionsApiService;
_analyticsService = analyticsService;
_preferences = preferences;
_analyticsService = analyticsService;
_azureFunctionsApiService = azureFunctionsApiService;
}

public StreamingManifest? OnboardingChart
Expand Down

0 comments on commit 26014eb

Please sign in to comment.