diff --git a/ServerlessLibraryAPI/CacheService.cs b/ServerlessLibraryAPI/CacheService.cs index d668a7ca..4d40c5ff 100644 --- a/ServerlessLibraryAPI/CacheService.cs +++ b/ServerlessLibraryAPI/CacheService.cs @@ -9,127 +9,127 @@ namespace ServerlessLibrary { - public interface ICacheService + public interface ICacheService + { + IList GetCachedItems(); + } + + //https://stackoverflow.com/questions/44723017/in-memory-caching-with-auto-regeneration-on-asp-net-core + public class CacheService : ICacheService + { + protected readonly IMemoryCache _cache; + private readonly ILibraryStore libraryStore; + private readonly ILogger logger; + + private Task LoadingTask = Task.CompletedTask; + private Timer Timer = null; + private bool LoadingBusy = false; + private bool isCacheLoadedOnce = false; + + public CacheService(IMemoryCache cache, ILibraryStore libraryStore, ILogger logger) { - IList GetCachedItems(); + this._cache = cache; + this.libraryStore = libraryStore; + this.logger = logger; + InitTimer(); } - //https://stackoverflow.com/questions/44723017/in-memory-caching-with-auto-regeneration-on-asp-net-core - public class CacheService : ICacheService + private void InitTimer() { - protected readonly IMemoryCache _cache; - private readonly ILibraryStore libraryStore; - private readonly ILogger logger; + _cache.Set(ServerlessLibrarySettings.CACHE_ENTRY, new LibraryItemsResult() { Result = new List(), IsBusy = true }); - private Task LoadingTask = Task.CompletedTask; - private Timer Timer = null; - private bool LoadingBusy = false; - private bool isCacheLoadedOnce = false; - - public CacheService(IMemoryCache cache, ILibraryStore libraryStore, ILogger logger) - { - this._cache = cache; - this.libraryStore = libraryStore; - this.logger = logger; - InitTimer(); - } + Timer = new Timer(TimerTickAsync, null, 1000, ServerlessLibrarySettings.SLCacheRefreshIntervalInSeconds * 1000); + } - private void InitTimer() + public IList GetCachedItems() + { + // Make a blocking call to load cache on first time call. + if (!isCacheLoadedOnce) + { + try { - _cache.Set(ServerlessLibrarySettings.CACHE_ENTRY, new LibraryItemsResult() { Result = new List(), IsBusy = true }); - - Timer = new Timer(TimerTickAsync, null, 1000, ServerlessLibrarySettings.SLCacheRefreshIntervalInSeconds * 1000); + logger.LogInformation("Loading initial cache"); + IList items = this.ConstructCache().Result; + _cache.Set(ServerlessLibrarySettings.CACHE_ENTRY, new LibraryItemsResult() { Result = items, IsBusy = false }); + logger.LogInformation("Loaded {0} items into cache", items.Count()); } - - public IList GetCachedItems() + catch (Exception ex) { - // Make a blocking call to load cache on first time call. - if (!isCacheLoadedOnce) - { - try - { - logger.LogInformation("Loading initial cache"); - IList items = this.ConstructCache().Result; - _cache.Set(ServerlessLibrarySettings.CACHE_ENTRY, new LibraryItemsResult() { Result = items, IsBusy = false }); - logger.LogInformation("Loaded {0} items into cache", items.Count()); - } - catch (Exception ex) - { - this.logger.LogError(ex, "Failed to load cache in first call"); - } - } - - logger.LogInformation("Successfully loaded initial cache"); - isCacheLoadedOnce = true; - return _cache.Get(ServerlessLibrarySettings.CACHE_ENTRY).Result; + this.logger.LogError(ex, "Failed to load cache in first call"); } + } - private async void TimerTickAsync(object state) - { - logger.LogInformation("Cache refresh timer fired"); - if (!isCacheLoadedOnce || LoadingBusy) - { - logger.LogWarning("Skipping cache refresh"); - return; - } + logger.LogInformation("Successfully loaded initial cache"); + isCacheLoadedOnce = true; + return _cache.Get(ServerlessLibrarySettings.CACHE_ENTRY).Result; + } - try - { - LoadingBusy = true; - LoadingTask = LoadCaches(); - await LoadingTask; - } - catch - { - // do not crash the app - } - finally - { - LoadingBusy = false; - } - } - private async Task LoadCaches() - { - try - { - logger.LogInformation("Starting cache refresh"); - var items = await ConstructCache(); - _cache.Set(ServerlessLibrarySettings.CACHE_ENTRY, new LibraryItemsResult() { Result = items, IsBusy = false }); - logger.LogInformation("Updated cache with {0} items", items.Count()); - } - catch (Exception ex) - { - this.logger.LogError(ex, "Failed to load cache"); - } - } - private async Task> ConstructCache() - { - logger.LogInformation("Starting ConstructCache"); - IList libraryItems; - IList libraryItemsWithStats = new List(); - libraryItems = await this.libraryStore.GetAllItems(); - logger.LogInformation("Cosmos DB returned {0} results", libraryItems.Count()); - var stats = await StorageHelper.getSLItemRecordsAsync(); - logger.LogInformation("Storage returned {0} results", stats.Count()); - foreach (var storeItem in libraryItems) - { - var item = storeItem.ConvertTo(); - var itemStat = stats.Where(s => s.id == storeItem.Id.ToString()).FirstOrDefault(); - item.TotalDownloads = itemStat != null && itemStat.totalDownloads > 0 ? itemStat.totalDownloads : 1; - item.Likes = itemStat != null && itemStat.likes > 0 ? itemStat.likes : 0; - item.Dislikes = itemStat != null && itemStat.dislikes > 0 ? itemStat.dislikes : 0; - libraryItemsWithStats.Add(item); - } + private async void TimerTickAsync(object state) + { + logger.LogInformation("Cache refresh timer fired"); + if (!isCacheLoadedOnce || LoadingBusy) + { + logger.LogWarning("Skipping cache refresh"); + return; + } - logger.LogInformation("ConstructCache returned {0} items", libraryItemsWithStats.Count()); - return libraryItemsWithStats; - } + try + { + LoadingBusy = true; + LoadingTask = LoadCaches(); + await LoadingTask; + } + catch + { + // do not crash the app + } + finally + { + LoadingBusy = false; + } } - - public class LibraryItemsResult + private async Task LoadCaches() + { + try + { + logger.LogInformation("Starting cache refresh"); + var items = await ConstructCache(); + _cache.Set(ServerlessLibrarySettings.CACHE_ENTRY, new LibraryItemsResult() { Result = items, IsBusy = false }); + logger.LogInformation("Updated cache with {0} items", items.Count()); + } + catch (Exception ex) + { + this.logger.LogError(ex, "Failed to load cache"); + } + } + private async Task> ConstructCache() { - public IList Result { get; set; } - public bool IsBusy { get; set; } + logger.LogInformation("Starting ConstructCache"); + IList libraryItems; + IList libraryItemsWithStats = new List(); + libraryItems = await this.libraryStore.GetAllItems(); + logger.LogInformation("Cosmos DB returned {0} results", libraryItems.Count()); + var stats = await StorageHelper.getSLItemRecordsAsync(); + logger.LogInformation("Storage returned {0} results", stats.Count()); + foreach (var storeItem in libraryItems) + { + var item = storeItem.ConvertTo(); + var itemStat = stats.Where(s => s.id == storeItem.Id.ToString()).FirstOrDefault(); + item.TotalDownloads = itemStat != null && itemStat.totalDownloads > 0 ? itemStat.totalDownloads : 1; + item.Likes = itemStat != null && itemStat.likes > 0 ? itemStat.likes : 0; + item.Dislikes = itemStat != null && itemStat.dislikes > 0 ? itemStat.dislikes : 0; + libraryItemsWithStats.Add(item); + } + + logger.LogInformation("ConstructCache returned {0} items", libraryItemsWithStats.Count()); + return libraryItemsWithStats; } + } + + public class LibraryItemsResult + { + public IList Result { get; set; } + public bool IsBusy { get; set; } + } } diff --git a/ServerlessLibraryAPI/ClientApp/src/components/DetailView/ActionBar.js b/ServerlessLibraryAPI/ClientApp/src/components/DetailView/ActionBar.js index a55bb97c..0a16d36b 100644 --- a/ServerlessLibraryAPI/ClientApp/src/components/DetailView/ActionBar.js +++ b/ServerlessLibraryAPI/ClientApp/src/components/DetailView/ActionBar.js @@ -44,11 +44,24 @@ class ActionBar extends Component { return "vscode://vscode.git/clone?url=" + encodeURIComponent(repository); } + getOpenInGithubDevLink(repository) { + if(repository){ + return repository.replace("github\.com", "github\.dev"); + } else { + return repository; + } + } + openInVSCodeClick() { this.updateDownloadCount(this.props.id); this.trackUserActionEvent("/sample/openinvscode"); } + openInGithubDevClick() { + this.updateDownloadCount(this.props.id); + this.trackUserActionEvent("/sample/openingithubdev"); + } + trackUserActionEvent(eventName) { let eventData = { id: this.props.id, @@ -76,6 +89,18 @@ class ActionBar extends Component { +
+ +
+ + Edit in GitHub.dev +
+
+