Controller methods should throw proper exceptions (#156)
* Controller methods should throw proper exceptions
This commit is contained in:
Родитель
c340384d85
Коммит
7af7f324f6
|
@ -0,0 +1,25 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="..\ServerlessLibraryAPI\CosmosLibraryStore.cs" Link="CosmosLibraryStore.cs" />
|
||||||
|
<Compile Include="..\ServerlessLibraryAPI\ILibraryStore.cs" Link="ILibraryStore.cs" />
|
||||||
|
<Compile Include="..\ServerlessLibraryAPI\Models\LibraryItem.cs" Link="LibraryItem.cs" />
|
||||||
|
<Compile Include="..\ServerlessLibraryAPI\ServerlessLibrarySettings.cs" Link="ServerlessLibrarySettings.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="..\ServerlessLibraryAPI\wwwroot\items.json" Link="items.json" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Azure.DocumentDB.Core" Version="2.3.0" />
|
||||||
|
<PackageReference Include="System.Configuration.ConfigurationManager" Version="4.5.0" />
|
||||||
|
<PackageReference Include="WindowsAzure.Storage" Version="9.3.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -0,0 +1,135 @@
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.IO;
|
||||||
|
using ServerlessLibrary.Models;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using ServerlessLibrary;
|
||||||
|
using Microsoft.WindowsAzure.Storage.Table;
|
||||||
|
using Microsoft.WindowsAzure.Storage;
|
||||||
|
using Microsoft.WindowsAzure.Storage.RetryPolicies;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace LibraryStoreMigration
|
||||||
|
{
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
const string tableName = "slitemstats";
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length < 1)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Please enter specify operation to be performed (cosmosdb|stats)");
|
||||||
|
Console.WriteLine("Please note that connection informations need to be provided as environment variables.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args[0].Equals("cosmosdb", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
MigrateToCosmosDB();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args[0].Equals("stats", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
AddNewStatsColumns();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AddNewStatsColumns()
|
||||||
|
{
|
||||||
|
TableRequestOptions tableRequestRetry = new TableRequestOptions { RetryPolicy = new LinearRetry(TimeSpan.FromSeconds(2), 3) };
|
||||||
|
TableQuery<NewSLItemStats> query = new TableQuery<NewSLItemStats>();
|
||||||
|
TableContinuationToken continuationToken = null;
|
||||||
|
List<NewSLItemStats> entities = new List<NewSLItemStats>();
|
||||||
|
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(ServerlessLibrarySettings.SLStorageString);
|
||||||
|
|
||||||
|
CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
|
||||||
|
CloudTable table = tableClient.GetTableReference(tableName);
|
||||||
|
var opContext = new OperationContext();
|
||||||
|
do
|
||||||
|
{
|
||||||
|
TableQuerySegment<NewSLItemStats> queryResults = (table).ExecuteQuerySegmentedAsync(query, continuationToken, tableRequestRetry, opContext).Result;
|
||||||
|
continuationToken = queryResults.ContinuationToken;
|
||||||
|
entities.AddRange(queryResults.Results);
|
||||||
|
} while (continuationToken != null);
|
||||||
|
|
||||||
|
Console.WriteLine(entities.Count);
|
||||||
|
List<LibraryItem> libraryItems = GetAllLibraryItemsFromFile();
|
||||||
|
foreach (var entity in entities)
|
||||||
|
{
|
||||||
|
entity.id = libraryItems.FirstOrDefault(l => l.Template == entity.template).Id;
|
||||||
|
entity.likes = 0;
|
||||||
|
entity.dislikes = 0;
|
||||||
|
TableOperation operation = TableOperation.InsertOrMerge(entity);
|
||||||
|
Task<TableResult> r = table.ExecuteAsync(operation);
|
||||||
|
TableResult a = r.Result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void MigrateToCosmosDB()
|
||||||
|
{
|
||||||
|
var libraryItems = GetAllLibraryItemsFromFile();
|
||||||
|
Console.WriteLine("Number of samples to be migrated from file to cosmos db: {0}", libraryItems.Count);
|
||||||
|
CosmosLibraryStore libraryStore = new CosmosLibraryStore();
|
||||||
|
|
||||||
|
IList<LibraryItem> libraryItemsInCosmos = libraryStore.GetAllItems().Result;
|
||||||
|
Console.WriteLine("Number of samples already present in cosmos db: {0}", libraryItemsInCosmos.Count);
|
||||||
|
|
||||||
|
if (libraryItemsInCosmos.Count != libraryItems.Count)
|
||||||
|
{
|
||||||
|
foreach (LibraryItem libraryItem in libraryItems)
|
||||||
|
{
|
||||||
|
if (!libraryItemsInCosmos.Any(c => c.Id == libraryItem.Id))
|
||||||
|
{
|
||||||
|
Console.WriteLine("Item {0} not present in cosmos db. will be migrated" + libraryItem.Id);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
libraryStore.Add(libraryItem).Wait();
|
||||||
|
Console.WriteLine("Migrated sample with id {0}" + libraryItem.Id);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Got exception {0}", ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine("Samples are successfully migrated to cosmos db");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("Samples are already migrated to cosmos db");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<LibraryItem> GetAllLibraryItemsFromFile()
|
||||||
|
{
|
||||||
|
var assembly = Assembly.GetExecutingAssembly();
|
||||||
|
using (Stream stream = assembly.GetManifestResourceStream("LibraryStoreMigration.items.json"))
|
||||||
|
using (StreamReader reader = new StreamReader(stream))
|
||||||
|
{
|
||||||
|
string result = reader.ReadToEnd();
|
||||||
|
return JsonConvert.DeserializeObject<List<LibraryItem>>(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OldSLItemStats : TableEntity
|
||||||
|
{
|
||||||
|
public string template { get; set; }
|
||||||
|
public int totalDownloads { get; set; }
|
||||||
|
public int downloadsToday { get; set; }
|
||||||
|
public int downloadsThisWeek { get; set; }
|
||||||
|
public int downloadsThisMonth { get; set; }
|
||||||
|
public DateTime lastUpdated { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class NewSLItemStats : OldSLItemStats
|
||||||
|
{
|
||||||
|
public string id { get; set; }
|
||||||
|
public int likes { get; set; }
|
||||||
|
public int dislikes { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -73,34 +73,7 @@ namespace ServerlessLibrary
|
||||||
{
|
{
|
||||||
IList<LibraryItem> libraryItems;
|
IList<LibraryItem> libraryItems;
|
||||||
IList<LibraryItemWithStats> libraryItemsWithStats = new List<LibraryItemWithStats>();
|
IList<LibraryItemWithStats> libraryItemsWithStats = new List<LibraryItemWithStats>();
|
||||||
|
|
||||||
if (cosmosDBInitialized)
|
|
||||||
{
|
|
||||||
libraryItems = await this.libraryStore.GetAllItems();
|
libraryItems = await this.libraryStore.GetAllItems();
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
libraryItems = await new FileLibraryStore(_env).GetAllItems();
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(ServerlessLibrarySettings.CosmosEndpoint))
|
|
||||||
{
|
|
||||||
IList<LibraryItem> libraryItemsInCosmos = await this.libraryStore.GetAllItems();
|
|
||||||
if (libraryItemsInCosmos.Count == 0)
|
|
||||||
{
|
|
||||||
foreach (LibraryItem libraryItem in libraryItems)
|
|
||||||
{
|
|
||||||
this.libraryStore.Add(libraryItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
libraryItems = libraryItemsInCosmos;
|
|
||||||
}
|
|
||||||
|
|
||||||
cosmosDBInitialized = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var stats = await StorageHelper.getSLItemRecordsAsync();
|
var stats = await StorageHelper.getSLItemRecordsAsync();
|
||||||
foreach (var storeItem in libraryItems)
|
foreach (var storeItem in libraryItems)
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using ServerlessLibrary.Models;
|
using ServerlessLibrary.Models;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
|
||||||
// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
|
// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
|
||||||
|
@ -53,7 +54,7 @@ namespace ServerlessLibrary.Controllers
|
||||||
|
|
||||||
[HttpPut]
|
[HttpPut]
|
||||||
[ProducesResponseType(typeof(LibraryItem), 200)]
|
[ProducesResponseType(typeof(LibraryItem), 200)]
|
||||||
public IActionResult Put([FromBody]LibraryItem libraryItem)
|
public async Task<IActionResult> Put([FromBody]LibraryItem libraryItem)
|
||||||
{
|
{
|
||||||
if (!User.Identity.IsAuthenticated)
|
if (!User.Identity.IsAuthenticated)
|
||||||
{
|
{
|
||||||
|
@ -74,7 +75,7 @@ namespace ServerlessLibrary.Controllers
|
||||||
GitHubUser user = new GitHubUser(User);
|
GitHubUser user = new GitHubUser(User);
|
||||||
libraryItem.Author = user.UserName;
|
libraryItem.Author = user.UserName;
|
||||||
|
|
||||||
StorageHelper.submitContributionForApproval(JsonConvert.SerializeObject(libraryItem));
|
await StorageHelper.submitContributionForApproval(JsonConvert.SerializeObject(libraryItem));
|
||||||
return new JsonResult(libraryItem);
|
return new JsonResult(libraryItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using ServerlessLibrary.Models;
|
using ServerlessLibrary.Models;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace ServerlessLibrary.Controllers
|
namespace ServerlessLibrary.Controllers
|
||||||
{
|
{
|
||||||
|
@ -8,12 +10,26 @@ namespace ServerlessLibrary.Controllers
|
||||||
[ApiController]
|
[ApiController]
|
||||||
public class MetricsController : ControllerBase
|
public class MetricsController : ControllerBase
|
||||||
{
|
{
|
||||||
|
private ILogger<MetricsController> logger;
|
||||||
|
public MetricsController(ILogger<MetricsController> logger)
|
||||||
|
{
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
// PUT api/<controller>/downloads
|
// PUT api/<controller>/downloads
|
||||||
[ProducesResponseType(typeof(bool), 200)]
|
[ProducesResponseType(typeof(bool), 200)]
|
||||||
[HttpPut]
|
[HttpPut]
|
||||||
public JsonResult Downloads([FromBody]string id)
|
public JsonResult Downloads([FromBody]string id)
|
||||||
{
|
{
|
||||||
StorageHelper.updateUserStats(JsonConvert.SerializeObject(new { id, userAction = "download" }));
|
try
|
||||||
|
{
|
||||||
|
StorageHelper.updateUserStats(JsonConvert.SerializeObject(new { id, userAction = "download" })).Wait();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.LogError("Unable to update download count. Exception: {0}", ex.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
return new JsonResult(true);
|
return new JsonResult(true);
|
||||||
}
|
}
|
||||||
// PUT api/<controller>/sentiment
|
// PUT api/<controller>/sentiment
|
||||||
|
@ -28,13 +44,21 @@ namespace ServerlessLibrary.Controllers
|
||||||
return BadRequest("Invalid values for like or dislike count");
|
return BadRequest("Invalid values for like or dislike count");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
StorageHelper.updateUserStats(JsonConvert.SerializeObject(new
|
StorageHelper.updateUserStats(JsonConvert.SerializeObject(new
|
||||||
{
|
{
|
||||||
id = sentimentPayload.Id,
|
id = sentimentPayload.Id,
|
||||||
userAction = "Sentiment",
|
userAction = "Sentiment",
|
||||||
likeChanges = sentimentPayload.LikeChanges,
|
likeChanges = sentimentPayload.LikeChanges,
|
||||||
dislikeChanges = sentimentPayload.DislikeChanges
|
dislikeChanges = sentimentPayload.DislikeChanges
|
||||||
}));
|
})).Wait();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.LogError("Unable to update Sentiments. Exception: {0}", ex.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
return new JsonResult(true);
|
return new JsonResult(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,14 +16,11 @@ namespace ServerlessLibrary
|
||||||
public class CosmosLibraryStore : ILibraryStore
|
public class CosmosLibraryStore : ILibraryStore
|
||||||
{
|
{
|
||||||
public CosmosLibraryStore()
|
public CosmosLibraryStore()
|
||||||
{
|
|
||||||
if (!string.IsNullOrWhiteSpace(ServerlessLibrarySettings.CosmosEndpoint))
|
|
||||||
{
|
{
|
||||||
CosmosDBRepository<LibraryItem>.Initialize();
|
CosmosDBRepository<LibraryItem>.Initialize();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async public void Add(LibraryItem libraryItem)
|
public async Task Add(LibraryItem libraryItem)
|
||||||
{
|
{
|
||||||
await CosmosDBRepository<LibraryItem>.CreateItemAsync(libraryItem);
|
await CosmosDBRepository<LibraryItem>.CreateItemAsync(libraryItem);
|
||||||
}
|
}
|
||||||
|
@ -35,7 +32,6 @@ namespace ServerlessLibrary
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Cosmos db APIs
|
/// Cosmos db APIs
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -20,7 +20,7 @@ namespace ServerlessLibrary
|
||||||
_env = env;
|
_env = env;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Add(LibraryItem libraryItem)
|
public Task Add(LibraryItem libraryItem)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace ServerlessLibrary
|
||||||
/// Add an item to library
|
/// Add an item to library
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="libraryItem">Library item </param>
|
/// <param name="libraryItem">Library item </param>
|
||||||
void Add(LibraryItem libraryItem);
|
Task Add(LibraryItem libraryItem);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get all items from library
|
/// Get all items from library
|
||||||
|
|
|
@ -25,7 +25,6 @@ namespace ServerlessLibrary
|
||||||
public static string CosmosAuthkey { get { return config(); } }
|
public static string CosmosAuthkey { get { return config(); } }
|
||||||
public static string Database { get { return "serverlesslibrary"; } }
|
public static string Database { get { return "serverlesslibrary"; } }
|
||||||
public static string Collection { get { return "contributions"; } }
|
public static string Collection { get { return "contributions"; } }
|
||||||
public static bool ApiOnly { get { return Boolean.Parse(config("false")); } }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer;
|
using Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using ServerlessLibrary.OAuth.GitHub;
|
using ServerlessLibrary.OAuth.GitHub;
|
||||||
using Swashbuckle.AspNetCore.Swagger;
|
using Swashbuckle.AspNetCore.Swagger;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace ServerlessLibrary
|
namespace ServerlessLibrary
|
||||||
{
|
{
|
||||||
|
@ -62,13 +65,10 @@ namespace ServerlessLibrary
|
||||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
||||||
{
|
{
|
||||||
app.UseExceptionHandler("/Error");
|
|
||||||
app.UseHsts();
|
app.UseHsts();
|
||||||
|
|
||||||
app.UseDefaultFiles();
|
app.UseDefaultFiles();
|
||||||
app.UseStaticFiles();
|
app.UseStaticFiles();
|
||||||
app.UseSpaStaticFiles();
|
app.UseSpaStaticFiles();
|
||||||
|
|
||||||
app.UseSwaggerUI(c =>
|
app.UseSwaggerUI(c =>
|
||||||
{
|
{
|
||||||
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Serverless library API v1");
|
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Serverless library API v1");
|
||||||
|
@ -84,8 +84,6 @@ namespace ServerlessLibrary
|
||||||
template: "{controller}/{action=Index}/{id?}");
|
template: "{controller}/{action=Index}/{id?}");
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!ServerlessLibrarySettings.ApiOnly)
|
|
||||||
{
|
|
||||||
app.UseSpa(spa =>
|
app.UseSpa(spa =>
|
||||||
{
|
{
|
||||||
spa.Options.SourcePath = "ClientApp";
|
spa.Options.SourcePath = "ClientApp";
|
||||||
|
@ -95,7 +93,6 @@ namespace ServerlessLibrary
|
||||||
spa.UseReactDevelopmentServer(npmScript: "start");
|
spa.UseReactDevelopmentServer(npmScript: "start");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
app.Use(async (context, next) =>
|
app.Use(async (context, next) =>
|
||||||
{
|
{
|
||||||
|
|
|
@ -52,13 +52,13 @@ namespace ServerlessLibrary
|
||||||
return queue;
|
return queue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async void submitContributionForApproval(string contributionPayload)
|
public static async Task submitContributionForApproval(string contributionPayload)
|
||||||
{
|
{
|
||||||
var message = new CloudQueueMessage(contributionPayload);
|
var message = new CloudQueueMessage(contributionPayload);
|
||||||
await (await getQueueReference(slContributionRequests)).AddMessageAsync(message);
|
await (await getQueueReference(slContributionRequests)).AddMessageAsync(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async void updateUserStats(string statsPayload)
|
public static async Task updateUserStats(string statsPayload)
|
||||||
{
|
{
|
||||||
var message = new CloudQueueMessage(statsPayload);
|
var message = new CloudQueueMessage(statsPayload);
|
||||||
await (await getQueueReference(slItemTableName)).AddMessageAsync(message);
|
await (await getQueueReference(slItemTableName)).AddMessageAsync(message);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче