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

Deploy Twitter integration to PROD. #97

Merged
merged 7 commits into from
Aug 23, 2024
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
2 changes: 1 addition & 1 deletion .github/workflows/api-deploy-PROD.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
secrets: inherit
with:
project-name: Api
dotnet-version: '6.0.x'
dotnet-version: '8.0.x'
service-name: ${{ needs.fetch-vars.outputs.service_name }}
service-path: ${{ needs.fetch-vars.outputs.service_path }}
environment: PROD
2 changes: 1 addition & 1 deletion .github/workflows/api-deploy-staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
secrets: inherit
with:
project-name: Api
dotnet-version: '6.0.x'
dotnet-version: '8.0.x'
service-name: ${{ needs.fetch-vars.outputs.service_name }}
service-path: ${{ needs.fetch-vars.outputs.service_path }}
environment: staging
10 changes: 3 additions & 7 deletions Api/Api.csproj
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>15373697-caf3-4a5a-b976-46077b7bff45</UserSecretsId>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<DockerComposeProjectPath>..\docker-compose.dcproj</DockerComposeProjectPath>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.9.1" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.4" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Application\Application.csproj" />
<ProjectReference Include="..\Infrastructure.MongoDB\Infrastructure.MongoDB.csproj" />
<ProjectReference Include="..\Infrastructure\Infrastructure.csproj" />
</ItemGroup>

</Project>
</Project>
29 changes: 27 additions & 2 deletions Api/Controllers/GeoLocationsController.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
using Application.Services;
using Api.Utilities;
using Application.Services;
using Core.Dto.Request;
using Core.Dto.Response;
using Core.Entities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Net;

namespace Api.Controllers
{
[Route("api/v1/[controller]")]
[ApiController]
[Authorize(Policy = "AdminAndProLexicographers")]
public class GeoLocationsController : ControllerBase
{
private readonly GeoLocationsService _geoLocationsService;
Expand All @@ -23,11 +27,32 @@ public GeoLocationsController(GeoLocationsService geoLocationsService)
/// An <see cref="GeoLocation[]"/> representing the response containing the list of <see cref="GeoLocation"/> objects.
/// </returns>
[HttpGet]
[AllowAnonymous]
[ProducesResponseType(typeof(GeoLocationDto[]), (int)HttpStatusCode.OK)]
public async Task<IActionResult> ListGeoLocations()
{
var result = (await _geoLocationsService.GetAll()).Select(g => new GeoLocationDto(g.Place, g.Region));
var result = (await _geoLocationsService.GetAll()).Select(g => new GeoLocationDto(g.Id, g.Place, g.Region));
return Ok(result);
}

[HttpPost]
[ProducesResponseType(typeof(GeoLocationDto), (int)HttpStatusCode.OK)]
public async Task<IActionResult> Create(CreateGeoLocationDto geo)
{
var geoLocation = new GeoLocation(geo.Place, geo.Region)
{
CreatedBy = User!.Identity!.Name!
};
await _geoLocationsService.Create(geoLocation);
return StatusCode((int)HttpStatusCode.Created, ResponseHelper.GetResponseDict("Geolocation successfully added"));
}

[HttpDelete("{id}/{place}")]
[ProducesResponseType(typeof(GeoLocationDto), (int)HttpStatusCode.OK)]
public async Task<IActionResult> Delete(string id, string place)
{
await _geoLocationsService.Delete(id, place);
return Ok(ResponseHelper.GetResponseDict($"Geolocation '{place}' successfully deleted"));
}
}
}
5 changes: 3 additions & 2 deletions Api/Controllers/SuggestedNameController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Net;
using Application.Mappers;
using Application.Validation;
using FluentValidation;

namespace Api.Controllers;

Expand All @@ -16,9 +17,9 @@ namespace Api.Controllers;
public class SuggestedNameController : ControllerBase
{
private readonly SuggestedNameService _suggestedNameService;
private readonly CreateSuggestedNameValidator _suggestedNameValidator;
private readonly IValidator<CreateSuggestedNameDto> _suggestedNameValidator;

public SuggestedNameController(SuggestedNameService suggestedNameService, CreateSuggestedNameValidator suggestedNameValidator)
public SuggestedNameController(SuggestedNameService suggestedNameService, IValidator<CreateSuggestedNameDto> suggestedNameValidator)
{
_suggestedNameService = suggestedNameService;
_suggestedNameValidator = suggestedNameValidator;
Expand Down
4 changes: 2 additions & 2 deletions Api/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.

FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["Api/Api.csproj", "Api/"]
RUN dotnet restore "Api/Api.csproj"
Expand Down
11 changes: 4 additions & 7 deletions Api/ExceptionHandler/GlobalExceptionHandling.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
namespace Api.ExceptionHandler
{
using Api.Utilities;
using Application.Exceptions;
using System.Net;
using System.Text.Json;
Expand Down Expand Up @@ -32,21 +33,17 @@ private async Task HandleExceptionAsync(HttpContext context, Exception exception
context.Response.ContentType = "application/json";
var response = context.Response;

var errorResponse = new ErrorResponse
{
Success = false
};
Dictionary<string, string> errorResponse;
switch (exception)
{
case ClientException ex:
response.StatusCode = (int)HttpStatusCode.BadRequest;
errorResponse.Message = ex.Message;
errorResponse = ResponseHelper.GetResponseDict(ex.Message);
break;


default:
response.StatusCode = (int)HttpStatusCode.InternalServerError;
errorResponse.Message = "Internal server error!";
errorResponse = ResponseHelper.GetResponseDict("Internal server error!");
break;
}
_logger.LogError(exception, "Unhandled Application Exception");
Expand Down
31 changes: 22 additions & 9 deletions Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,19 @@
using Core.Events;
using Core.StringObjectConverters;
using FluentValidation;
using Infrastructure;
using Infrastructure.MongoDB;
using Infrastructure.Services;
using Microsoft.AspNetCore.Authentication;
using Microsoft.OpenApi.Models;
using MySqlConnector;
using System.Collections.Concurrent;
using System.Text.Json.Serialization;

var builder = WebApplication.CreateBuilder(args);
var Configuration = builder.Configuration;
var configuration = builder.Configuration;
configuration.AddEnvironmentVariables("YND_");

string DevCORSAllowAll = "AllowAllForDev";
var services = builder.Services;

Expand Down Expand Up @@ -81,28 +86,36 @@
}
});
});
var mongoDbSettings = Configuration.GetSection("MongoDB");
var mongoDbSettings = configuration.GetSection("MongoDB");
services.InitializeDatabase(mongoDbSettings.GetValue<string>("ConnectionString"), mongoDbSettings.GetValue<string>("DatabaseName"));

builder.Services.AddTransient(x =>
new MySqlConnection(builder.Configuration.GetSection("MySQL:ConnectionString").Value));

services.AddScoped<NameEntryService>();
services.AddScoped<GeoLocationsService>();
services.AddScoped<NameEntryFeedbackService>();
services.AddScoped<IEventPubService, EventPubService>();
services.AddScoped<SearchService>();
services.AddScoped<SuggestedNameService>();
services.AddScoped<UserService>();
services.AddSingleton<NameEntryService>();
services.AddSingleton<GeoLocationsService>();
services.AddSingleton<NameEntryFeedbackService>();
services.AddSingleton<IEventPubService, EventPubService>();
services.AddSingleton<SearchService>();
services.AddSingleton<SuggestedNameService>();
services.AddSingleton<UserService>();
services.AddScoped<GeoLocationValidator>();
services.AddScoped<EmbeddedVideoValidator>();
services.AddScoped<EtymologyValidator>();
services.AddScoped<SqlToMongoMigrator>();
services.AddSingleton<IRecentIndexesCache, RecentIndexesCache>();
services.AddSingleton<IRecentSearchesCache, RecentSearchesCache>();

//Validation
services.AddValidatorsFromAssemblyContaining<CreateUserValidator>();

services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(ExactNameSearchedAdapter).Assembly));

// Twitter integration configuration
services.AddSingleton<ConcurrentQueue<PostPublishedNameCommand>>();
services.AddTwitterClient(configuration);
services.AddHostedService<NamePostingService>();


var app = builder.Build();

Expand Down
11 changes: 9 additions & 2 deletions Api/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
"Application.Services.BasicAuthenticationHandler": "Warning"
}
},
"AllowedHosts": "*"

"AllowedHosts": "*",
"Twitter": {
"AccessToken": "your-access-token",
"AccessTokenSecret": "your-access-token-secret",
"ConsumerKey": "your-consumer-key",
"ConsumerSecret": "your-consumer-secret",
"NameUrlPrefix": "https://www.yorubaname.com/entries",
"TweetTemplate": "New name entry: {name}, {meaning}. More here: {link}"
}
}
6 changes: 6 additions & 0 deletions Application/Application.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<Compile Remove="Interfaces\**" />
<EmbeddedResource Remove="Interfaces\**" />
<None Remove="Interfaces\**" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="Dapper" Version="2.1.35" />
Expand Down
38 changes: 38 additions & 0 deletions Application/EventHandlers/DeletedNameCachingHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Application.Events;
using Core.Cache;
using MediatR;
using Microsoft.Extensions.Logging;

namespace Application.EventHandlers
{
public class DeletedNameCachingHandler : INotificationHandler<NameDeletedAdapter>
{
private readonly IRecentIndexesCache _recentIndexesCache;
private readonly IRecentSearchesCache _recentSearchesCache;
private readonly ILogger<DeletedNameCachingHandler> _logger;

public DeletedNameCachingHandler(
IRecentIndexesCache recentIndexesCache,
IRecentSearchesCache recentSearchesCache,
ILogger<DeletedNameCachingHandler> logger
)
{
_recentIndexesCache = recentIndexesCache;
_recentSearchesCache = recentSearchesCache;
_logger = logger;
}

public async Task Handle(NameDeletedAdapter notification, CancellationToken cancellationToken)
{
try
{
await _recentIndexesCache.Remove(notification.Name);
await _recentSearchesCache.Remove(notification.Name);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error occurred while removing deleted name '{name}' from cache.", notification.Name);
}
}
}
}
38 changes: 0 additions & 38 deletions Application/EventHandlers/NameDeletedEventHandler.cs

This file was deleted.

7 changes: 6 additions & 1 deletion Application/EventHandlers/NameIndexedEventHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,20 @@ namespace Application.EventHandlers
public class NameIndexedEventHandler : INotificationHandler<NameIndexedAdapter>
{
public IRecentIndexesCache _recentIndexesCache;
private readonly IMediator _mediator;

public NameIndexedEventHandler(IRecentIndexesCache recentIndexesCache)
public NameIndexedEventHandler(
IRecentIndexesCache recentIndexesCache,
IMediator mediator)
{
_recentIndexesCache = recentIndexesCache;
_mediator = mediator;
}

public async Task Handle(NameIndexedAdapter notification, CancellationToken cancellationToken)
{
await _recentIndexesCache.Stack(notification.Name);
await _mediator.Publish(new PostPublishedNameCommand(notification.Name), cancellationToken);
}
}
}
25 changes: 25 additions & 0 deletions Application/EventHandlers/PostPublishedNameCommandHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Application.Events;
using MediatR;
using System.Collections.Concurrent;

namespace Application.EventHandlers
{
public class PostPublishedNameCommandHandler : INotificationHandler<PostPublishedNameCommand>
{
private readonly ConcurrentQueue<PostPublishedNameCommand> _nameQueue;

public PostPublishedNameCommandHandler(ConcurrentQueue<PostPublishedNameCommand> nameQueue)
{
_nameQueue = nameQueue;
}

public Task Handle(PostPublishedNameCommand notification, CancellationToken cancellationToken)
{
// Enqueue the indexed name for processing by the BackgroundService
_nameQueue.Enqueue(notification);

// Return a completed task, so it doesn't block the main thread
return Task.CompletedTask;
}
}
}
8 changes: 8 additions & 0 deletions Application/Events/PostPublishedNameCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using MediatR;

namespace Application.Events
{
public record PostPublishedNameCommand(string Name) : INotification
{
}
}
Loading
Loading