Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Лиясов, ФТ302 #13

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
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
44 changes: 44 additions & 0 deletions BadNews/Components/ArchiveLinksViewComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using BadNews.Repositories.News;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections.Generic;

namespace BadNews.Components
{
public class ArchiveLinksViewComponent : ViewComponent
{
private readonly INewsRepository newsRepository;
private readonly IMemoryCache memoryCache;

public ArchiveLinksViewComponent(INewsRepository newsRepository, IMemoryCache memoryCache)
{
this.newsRepository = newsRepository;
this.memoryCache = memoryCache;
}

public IViewComponentResult Invoke()
{
var years = GetYearsWithArticles();
return View(years);
}

private IList<int> GetYearsWithArticles()
{
const string cacheKey = nameof(ArchiveLinksViewComponent) + "_" + nameof(GetYearsWithArticles);
if (!memoryCache.TryGetValue(cacheKey, out var years))
{
years = newsRepository.GetYearsWithArticles();
if (years != null)
{
memoryCache.Set(cacheKey, years, new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(30)
});
}
}

return (IList<int>)years;
}
}
}
22 changes: 22 additions & 0 deletions BadNews/Components/WeatherViewComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using BadNews.Repositories.Weather;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;

namespace BadNews.Components
{
public class WeatherViewComponent : ViewComponent
{
private readonly IWeatherForecastRepository weatherRepository;

public WeatherViewComponent(IWeatherForecastRepository weatherRepository)
{
this.weatherRepository = weatherRepository;
}

public async Task<IViewComponentResult> InvokeAsync()
{
var weather = await weatherRepository.GetWeatherForecastAsync();
return View(weather);
}
}
}
52 changes: 52 additions & 0 deletions BadNews/Controllers/EditorController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System;
using BadNews.Elevation;
using BadNews.Models.Editor;
using BadNews.Repositories.News;
using Microsoft.AspNetCore.Mvc;

namespace BadNews.Controllers
{
[ElevationRequiredFilter]
public class EditorController : Controller
{
private readonly INewsRepository newsRepository;

public EditorController(INewsRepository newsRepository)
{
this.newsRepository = newsRepository;
}

[HttpPost]
public IActionResult CreateArticle([FromForm] IndexViewModel model)
{
if (!ModelState.IsValid)
return View("Index", model);

var id = newsRepository.CreateArticle(new NewsArticle
{
Date = DateTime.Now.Date,
Header = model.Header,
Teaser = model.Teaser,
ContentHtml = model.ContentHtml,
});

return RedirectToAction("FullArticle", "News", new
{
id = id
});
}

[HttpPost]
public IActionResult DeleteArticle(Guid id)
{
newsRepository.DeleteArticleById(id);
return RedirectToAction("Index", "News");
}

[HttpGet("/editor")]
public IActionResult Index()
{
return View(new IndexViewModel());
}
}
}
27 changes: 27 additions & 0 deletions BadNews/Controllers/ErrorsController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;

namespace BadNews.Controllers
{
public class ErrorsController : Controller
{
private readonly ILogger<ErrorsController> logger;

public ErrorsController(ILogger<ErrorsController> logger)
{
this.logger = logger;
}

public IActionResult StatusCode(int? code)
{
logger.LogWarning("status-code {code} at {time}", code, DateTime.Now);
return View(code);
}

public IActionResult Exception()
{
return View(null, HttpContext.TraceIdentifier);
}
}
}
53 changes: 53 additions & 0 deletions BadNews/Controllers/NewsController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using BadNews.ModelBuilders.News;
using BadNews.Models.News;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using System;

namespace BadNews.Controllers
{
[ResponseCache(Duration = 30, Location = ResponseCacheLocation.Client, VaryByHeader = "Cookie")]
public class NewsController : Controller
{
private readonly INewsModelBuilder newsModelBuilder;
private readonly IMemoryCache memoryCache;

public NewsController(INewsModelBuilder newsModelBuilder, IMemoryCache memoryCache)
{
this.newsModelBuilder = newsModelBuilder;
this.memoryCache = memoryCache;
}

public IActionResult Index(int? year, int pageIndex = 0)
{
var model = newsModelBuilder.BuildIndexModel(pageIndex, true, year);
return View(model);
}

[ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult FullArticle(Guid id)
{
var model = BuildFullArticleModel(id);
return model == null ? NotFound() : View(model);
}

private FullArticleModel BuildFullArticleModel(Guid id)
{
const string cacheKeyBase = nameof(NewsController) + "_" + nameof(BuildFullArticleModel) + "_";
string cacheKey = cacheKeyBase + id;
if (!memoryCache.TryGetValue(cacheKey, out var years))
{
years = newsModelBuilder.BuildFullArticleModel(id);
if (years != null)
{
memoryCache.Set(cacheKey, years, new MemoryCacheEntryOptions
{
SlidingExpiration = TimeSpan.FromSeconds(30)
});
}
}

return (FullArticleModel)years;
}
}
}
2 changes: 2 additions & 0 deletions BadNews/Elevation/ElevationConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ namespace BadNews.Elevation
{
public static class ElevationConstants
{
public const string Path = "/elevation";
public const string QueryKey = "up";
public const string CookieName = "elevation";
public const string CookieValue = "yes";
}
Expand Down
13 changes: 12 additions & 1 deletion BadNews/Elevation/ElevationMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,18 @@ public ElevationMiddleware(RequestDelegate next)

public async Task InvokeAsync(HttpContext context)
{
throw new NotImplementedException();
if (string.Equals(context.Request.Path, ElevationConstants.Path, StringComparison.OrdinalIgnoreCase))
{
if (context.Request.Query.ContainsKey(ElevationConstants.QueryKey))
context.Response.Cookies.Append(ElevationConstants.CookieName,
ElevationConstants.CookieValue, new CookieOptions { HttpOnly = true });
else
context.Response.Cookies.Delete(ElevationConstants.CookieName);

context.Response.Redirect("/");
}

await next(context);
}
}
}
34 changes: 33 additions & 1 deletion BadNews/Program.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
using System;
using System.Linq;
using BadNews.Repositories.News;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Serilog;

namespace BadNews
{
Expand All @@ -10,7 +13,31 @@ public class Program
public static void Main(string[] args)
{
InitializeDataBase();
CreateHostBuilder(args).Build().Run();

Log.Logger = new LoggerConfiguration()
.WriteTo.File(".logs/start-host-log-.txt",
rollingInterval: RollingInterval.Day,
rollOnFileSizeLimit: true,
outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {Properties:j}{NewLine}{Exception}")
.CreateLogger();

try
{
Log.Information("Creating web host builder");
var hostBuilder = CreateHostBuilder(args);
Log.Information("Building web host");
var host = hostBuilder.Build();
Log.Information("Running web host");
host.Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}
}

public static IHostBuilder CreateHostBuilder(string[] args)
Expand All @@ -19,6 +46,11 @@ public static IHostBuilder CreateHostBuilder(string[] args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.UseSerilog((hostingContext, loggerConfiguration) =>
loggerConfiguration.ReadFrom.Configuration(hostingContext.Configuration))
.ConfigureHostConfiguration(config => {
config.AddJsonFile("appsettings.Secret.json", optional: true, reloadOnChange: false);
});
}

Expand Down
6 changes: 3 additions & 3 deletions BadNews/Repositories/Weather/OpenWeatherForecast.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ public class WeatherInfo

public class MainInfo
{
public int Temp { get; set; }
public decimal Temp { get; set; }
public decimal FeelsLike { get; set; }
public int TempMin { get; set; }
public int TempMax { get; set; }
public decimal TempMin { get; set; }
public decimal TempMax { get; set; }
public int Pressure { get; set; }
public int Humidity { get; set; }
}
Expand Down
2 changes: 1 addition & 1 deletion BadNews/Repositories/Weather/WeatherForecast.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ public class WeatherForecast
{
private const string defaultWeatherImageUrl = "/images/cloudy.png";

public int TemperatureInCelsius { get; set; }
public decimal TemperatureInCelsius { get; set; }
public string IconUrl { get; set; }

public static WeatherForecast CreateFrom(OpenWeatherForecast forecast)
Expand Down
26 changes: 24 additions & 2 deletions BadNews/Repositories/Weather/WeatherForecastRepository.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Microsoft.Extensions.Options;
using System;
using System.Threading.Tasks;

Expand All @@ -6,12 +7,33 @@ namespace BadNews.Repositories.Weather
public class WeatherForecastRepository : IWeatherForecastRepository
{
private const string defaultWeatherImageUrl = "/images/cloudy.png";

private readonly string apiKey;
private readonly OpenWeatherClient openWeatherClient;
private readonly Random random = new Random();

public WeatherForecastRepository(IOptions<OpenWeatherOptions> weatherOptions)
{
apiKey = weatherOptions?.Value.ApiKey;
openWeatherClient = apiKey is not null ? new OpenWeatherClient(apiKey) : null;
}

public async Task<WeatherForecast> GetWeatherForecastAsync()
{
return BuildRandomForecast();
var serviceResponse = await TryGetWeatherFromOpenWeather();
if (serviceResponse is null) return BuildRandomForecast();
return WeatherForecast.CreateFrom(serviceResponse);
}

private async Task<OpenWeatherForecast> TryGetWeatherFromOpenWeather()
{
try
{
return await openWeatherClient?.GetWeatherFromApiAsync();
}
catch (Exception e)
{
return null;
}
}

private WeatherForecast BuildRandomForecast()
Expand Down
Loading