Skip to content

Commit

Permalink
#17 Add resources microservice (#60)
Browse files Browse the repository at this point in the history
Co-authored-by: burst <burstsangels@gmail.com>
  • Loading branch information
VasiliyKolihalov and burst authored Aug 28, 2023
1 parent 723ebcc commit e7812c8
Show file tree
Hide file tree
Showing 41 changed files with 806 additions and 10 deletions.
6 changes: 6 additions & 0 deletions HelpDeskSystem.sln
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ApiGateway", "ApiGateway\Ap
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SupportTickets.Tests", "SupportTickets.Tests\SupportTickets.Tests.csproj", "{7181D94D-3C51-4F3B-9B21-DAA1073F5EF3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Resources.WebApi", "Resources.WebApi\Resources.WebApi.csproj", "{40A3AFD7-680D-42E3-BE78-B2E3C90305D4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -59,5 +61,9 @@ Global
{7181D94D-3C51-4F3B-9B21-DAA1073F5EF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7181D94D-3C51-4F3B-9B21-DAA1073F5EF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7181D94D-3C51-4F3B-9B21-DAA1073F5EF3}.Release|Any CPU.Build.0 = Release|Any CPU
{40A3AFD7-680D-42E3-BE78-B2E3C90305D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{40A3AFD7-680D-42E3-BE78-B2E3C90305D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{40A3AFD7-680D-42E3-BE78-B2E3C90305D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{40A3AFD7-680D-42E3-BE78-B2E3C90305D4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
21 changes: 21 additions & 0 deletions Resources.WebApi/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
COPY ["Resources.WebApi/Resources.WebApi.csproj", "Resources.WebApi/"]
COPY ["Infrastructure/Infrastructure.csproj", "Infrastructure/"]
RUN dotnet restore "Resources.WebApi/Resources.WebApi.csproj"
COPY . .
WORKDIR "/src/Resources.WebApi"
RUN dotnet build "Resources.WebApi.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "Resources.WebApi.csproj" -c Release -o /app/publish /p:UseAppHost=false

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Resources.WebApi.dll"]
55 changes: 55 additions & 0 deletions Resources.WebApi/GrpcServices/GrpcImagesService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using AutoMapper;
using Grpc.Core;
using Infrastructure.Exceptions;
using Resources.WebApi.Models.Images;
using Resources.WebApi.Services;

namespace Resources.WebApi.GrpcServices;

public class GrpcImagesService : Images.ImagesBase
{
private readonly ImagesService _imagesService;
private readonly IMapper _mapper;

public GrpcImagesService(
ImagesService imagesService,
IMapper mapper)
{
_imagesService = imagesService;
_mapper = mapper;
}

public override async Task<GetMessageImagesResponse> GetByMessageId(
GetMessageImagesRequest request,
ServerCallContext _)
{
Guid messageId = Guid.Parse(request.MessageId);
IEnumerable<ImageView> imageViews;
try
{
imageViews = await _imagesService.GetByMessageIdAsync(messageId);
}
catch (NotFoundException exception)
{
throw new RpcException(new Status(StatusCode.NotFound, exception.Message));
}
var response = new GetMessageImagesResponse();
response.Images.AddRange(_mapper.Map<IEnumerable<ImageResponse>>(imageViews));
return response;
}

public override async Task<Empty> AddToMessage(AddToMessageImagesRequest request, ServerCallContext _)
{
Guid messageId = Guid.Parse(request.MessageId);
var imageCreates = _mapper.Map<IEnumerable<ImageCreate>>(request.Images);
try
{
await _imagesService.AddToMessageAsync(messageId, imageCreates);
}
catch (BadRequestException exception)
{
throw new RpcException(new Status(StatusCode.InvalidArgument, exception.Message));
}
return new Empty();
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

60 changes: 60 additions & 0 deletions Resources.WebApi/Migrations/20230828082440_Migration28082023.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace Resources.WebApi.Migrations
{
/// <inheritdoc />
public partial class Migration28082023 : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Images",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
Content = table.Column<byte[]>(type: "bytea", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Images", x => x.Id);
});

migrationBuilder.CreateTable(
name: "ImagesMessages",
columns: table => new
{
MessageId = table.Column<Guid>(type: "uuid", nullable: false),
ImageId = table.Column<Guid>(type: "uuid", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ImagesMessages", x => new { x.MessageId, x.ImageId });
table.ForeignKey(
name: "FK_ImagesMessages_Images_ImageId",
column: x => x.ImageId,
principalTable: "Images",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});

migrationBuilder.CreateIndex(
name: "IX_ImagesMessages_ImageId",
table: "ImagesMessages",
column: "ImageId");
}

/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "ImagesMessages");

migrationBuilder.DropTable(
name: "Images");
}
}
}
67 changes: 67 additions & 0 deletions Resources.WebApi/Migrations/ApplicationContextModelSnapshot.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Resources.WebApi.Repositories;

#nullable disable

namespace Resources.WebApi.Migrations
{
[DbContext(typeof(ApplicationContext))]
partial class ApplicationContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.10")
.HasAnnotation("Relational:MaxIdentifierLength", 63);

NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);

modelBuilder.Entity("Resources.WebApi.Models.Images.Image", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<byte[]>("Content")
.IsRequired()
.HasColumnType("bytea");
b.HasKey("Id");
b.ToTable("Images");
});

modelBuilder.Entity("Resources.WebApi.Models.Images.ImageMessage", b =>
{
b.Property<Guid>("MessageId")
.HasColumnType("uuid");
b.Property<Guid>("ImageId")
.HasColumnType("uuid");
b.HasKey("MessageId", "ImageId");
b.HasIndex("ImageId");
b.ToTable("ImagesMessages");
});

modelBuilder.Entity("Resources.WebApi.Models.Images.ImageMessage", b =>
{
b.HasOne("Resources.WebApi.Models.Images.Image", "Image")
.WithMany()
.HasForeignKey("ImageId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Image");
});
#pragma warning restore 612, 618
}
}
}
10 changes: 10 additions & 0 deletions Resources.WebApi/Models/Images/Image.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.ComponentModel.DataAnnotations.Schema;

namespace Resources.WebApi.Models.Images;

public class Image
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public Guid Id { get; set; }
public byte[] Content { get; set; }

Check warning on line 9 in Resources.WebApi/Models/Images/Image.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Content' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
}
6 changes: 6 additions & 0 deletions Resources.WebApi/Models/Images/ImageCreate.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Resources.WebApi.Models.Images;

public class ImageCreate
{
public string Base64Content { get; set; }

Check warning on line 5 in Resources.WebApi/Models/Images/ImageCreate.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Base64Content' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
}
12 changes: 12 additions & 0 deletions Resources.WebApi/Models/Images/ImageMessage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Microsoft.EntityFrameworkCore;

namespace Resources.WebApi.Models.Images;

[PrimaryKey(nameof(MessageId), nameof(ImageId))]
public class ImageMessage
{
public Guid MessageId { get; set; }
public Guid ImageId { get; set; }

public Image Image { get; set; }

Check warning on line 11 in Resources.WebApi/Models/Images/ImageMessage.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Image' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
}
7 changes: 7 additions & 0 deletions Resources.WebApi/Models/Images/ImageView.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Resources.WebApi.Models.Images;

public class ImageView
{
public Guid Id { get; set; }
public string Base64Content { get; set; }

Check warning on line 6 in Resources.WebApi/Models/Images/ImageView.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Base64Content' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
}
18 changes: 18 additions & 0 deletions Resources.WebApi/Profiles/ImageProfile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using AutoMapper;
using Resources.WebApi.Models.Images;
using Resources.WebApi.Services;

namespace Resources.WebApi.Profiles;

public class ImageProfile : Profile
{
public ImageProfile()
{
CreateMap<Image, ImageView>()
.ForMember(
_ => _.Base64Content,
expression => expression.MapFrom(_ => Convert.ToBase64String(_.Content)));
CreateMap<ImageView, ImageResponse>();
CreateMap<ImageRequest, ImageCreate>();
}
}
33 changes: 33 additions & 0 deletions Resources.WebApi/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Infrastructure.Extensions;
using Microsoft.EntityFrameworkCore;
using Resources.WebApi.GrpcServices;
using Resources.WebApi.Repositories;
using Resources.WebApi.Services;

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
ConfigureServices(builder);
WebApplication app = builder.Build();
app.TriggerEntityFrameworkMigrations<ApplicationContext>();
ConfigureMiddlewares(app);

static void ConfigureServices(WebApplicationBuilder builder)
{
builder.Services.AddGrpc();

string connectionString = builder.Configuration.GetRequiredConnectionString("Default");
builder.Services
.AddDbContext<ApplicationContext>(options => options.UseNpgsql(connectionString))
.AddTransient<IImagesRepository, ImagesRepository>();

builder.Services
.AddAutoMapper(typeof(Program))
.AddTransient<ImagesService>()
.AddGrpc();
}

static void ConfigureMiddlewares(WebApplication app)
{
app.MapGrpcService<GrpcImagesService>();
app.MapGet("/", () => "Communication with gRPC endpoints must be made through a gRPC client.");
app.Run();
}
Loading

0 comments on commit e7812c8

Please sign in to comment.