Skip to content
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
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggest
#Style - Modifier preferences

#when this rule is set to a list of modifiers, prefer the specified ordering.
csharp_preferred_modifier_order = public,private,static,async,readonly:suggestion
csharp_preferred_modifier_order = public, private, protected, internal, static, extern, new, virtual, abstract, sealed, override, readonly, unsafe, volatile, async:suggestion

#Style - Pattern matching

Expand Down
23 changes: 23 additions & 0 deletions Authors.Grpc/Authors - Backup.Grpc.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.13.0" />
<PackageReference Include="Grpc.AspNetCore.Server" Version="2.32.0" />
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.32.0" />
<PackageReference Include="Grpc.Tools" Version="2.32.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<None Update="authors.proto">
<GrpcServices>Both</GrpcServices>
</None>
</ItemGroup>

</Project>
25 changes: 25 additions & 0 deletions Authors.Grpc/Authors.Grpc.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<None Remove="authors.proto" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.13.0" />
<PackageReference Include="Grpc.AspNetCore.Server" Version="2.32.0" />
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.32.0" />
<PackageReference Include="Grpc.Tools" Version="2.32.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<Protobuf Include="authors.proto" GrpcServices="Both" />
</ItemGroup>

</Project>
69 changes: 69 additions & 0 deletions Authors.Grpc/authors.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
syntax = "proto3";

option csharp_namespace="AuthorsService.Grpc";

service AuthorsServiceProto {
rpc GetAuthors(GetAuthorsRequest) returns (GetAuthorsResponse);
rpc GetAuthor(GetAuthorRequest) returns (GetAuthorResponse);
rpc UpdateAuthor(UpdateAuthorRequest) returns (UpdateAuthorResponse);
rpc CreateAuthor(CrateAuthorRequest) returns (CrateAuthorResponse);
rpc DeleteAuthor(DeleteAuthorRequest) returns (DeleteAuthorResponse);
rpc UpdateAuthorBooksCount(UpdateAuthorBooksCountRequest) returns (UpdateAuthorBooksCountResponse);
}

message Author {
string id = 1;
string firstName = 2;
string lastName = 3;
uint32 age = 4;
string biography = 5;
uint32 numberOfBooks = 6;
}

message GetAuthorsRequest {};

message GetAuthorsResponse {
repeated Author authors = 1;
};

message GetAuthorRequest {
string id = 1;
};

message GetAuthorResponse {
Author author = 1;
};

message UpdateAuthorRequest {
Author author = 1;
};

message UpdateAuthorResponse {};

message CrateAuthorRequest {
Author author = 1;
};

message CrateAuthorResponse {
string id = 1;
};

message DeleteAuthorRequest {
string id = 1;
}

message DeleteAuthorResponse {}

enum UpdateType {
None = 0;
Increase = 1;
Decrease = 2;
}

message UpdateAuthorBooksCountRequest {
string id = 1;
uint32 delta = 2;
UpdateType updateType = 3;
}

message UpdateAuthorBooksCountResponse {}
1 change: 1 addition & 0 deletions AuthorsService/AuthorsService.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Authors.Grpc\Authors.Grpc.csproj" />
<ProjectReference Include="..\SharedTypes.Api\SharedTypes.Api.csproj" />
</ItemGroup>

Expand Down
6 changes: 3 additions & 3 deletions AuthorsService/Controllers/AuthorsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ public async Task<IActionResult> UpdateAuthor(Guid id, AuthorDto authorDto, Canc
if (author is null) return this.NotFound();

author.Age = authorDto.Age;
author.Biography = author.Biography;
author.FirstName = author.FirstName;
author.LastName = author.LastName;
author.Biography = authorDto.Biography;
author.FirstName = authorDto.FirstName;
author.LastName = authorDto.LastName;
author.NumberOfBooks = authorDto.NumberOfBooks;
this.context.Entry(author).State = EntityState.Modified;

Expand Down
184 changes: 184 additions & 0 deletions AuthorsService/Controllers/AuthorsGrpcService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
using System;
using System.Linq;
using System.Threading.Tasks;

using AuthorsService.Extensions;
using AuthorsService.Grpc;
using AuthorsService.Models;

using Grpc.Core;

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

using Author = AuthorsService.Models.Author;

namespace AuthorsService.Controllers
{
public class AuthorsGrpcService : AuthorsServiceProto.AuthorsServiceProtoBase
{
private readonly AuthorContext context;
private readonly ILogger<AuthorsGrpcService> logger;

public AuthorsGrpcService(AuthorContext context, ILogger<AuthorsGrpcService> logger)
{
this.context = context;
this.logger = logger;
}

public override async Task<GetAuthorsResponse> GetAuthors(GetAuthorsRequest request,
ServerCallContext callContext)
{
try
{
var authors = await this.context.Authors.Select(a => a.ToGrpcDto())
.ToListAsync(callContext.CancellationToken);
GetAuthorsResponse response = new();
response.Authors.AddRange(authors);
return response;
}
catch (Exception e)
{
this.logger.LogError(e.Message);
throw new RpcException(new Status(StatusCode.Internal, e.Message));
}
}

public override async Task<GetAuthorResponse> GetAuthor(GetAuthorRequest request, ServerCallContext callContext)
{
try
{
var author =
await this.context.Authors.FindAsync(new object[] {request.Id}, callContext.CancellationToken);

if (author == null)
throw new RpcException(new Status(StatusCode.NotFound, $"There is no author with id {request.Id}"));

GetAuthorResponse response = new() {Author = author.ToGrpcDto()};
return response;
}
catch (Exception e)
{
this.logger.LogError(e.Message);
throw new RpcException(new Status(StatusCode.Internal, e.Message));
}
}

public override async Task<UpdateAuthorResponse>
UpdateAuthor(UpdateAuthorRequest request, ServerCallContext callContext)
{
var author =
await this.context.Authors.FindAsync(new object[] {request.Author.Id}, callContext.CancellationToken);

if (author is null)
{
throw new RpcException(new Status(StatusCode.NotFound,
$"There is no author with id {request.Author.Id}"));
}

author.Age = request.Author.Age;
author.Biography = request.Author.Biography;
author.FirstName = request.Author.FirstName;
author.LastName = request.Author.LastName;
author.NumberOfBooks = request.Author.NumberOfBooks;
this.context.Entry(author).State = EntityState.Modified;

try
{
await this.context.SaveChangesAsync(callContext.CancellationToken);
}
catch (DbUpdateConcurrencyException)
{
if (!this.context.Authors.Any(a => a.Id.ToString() == request.Author.Id))
{
throw new RpcException(new Status(StatusCode.NotFound,
$"There is no author with id {request.Author.Id}"));
}

throw new RpcException(new Status(StatusCode.Internal, "Couldn't update author"));
}

return new UpdateAuthorResponse();
}

public override async Task<CrateAuthorResponse> CreateAuthor(CrateAuthorRequest request,
ServerCallContext callContext)
{
var author = new Author {
Age = request.Author.Age,
Biography = request.Author.Biography,
FirstName = request.Author.FirstName,
Id = Guid.NewGuid(),
LastName = request.Author.LastName,
NumberOfBooks = request.Author.NumberOfBooks
};

try
{
this.context.Authors.Add(author);
await this.context.SaveChangesAsync(callContext.CancellationToken);
CrateAuthorResponse response = new() {Id = author.Id.ToString()};
return response;
}
catch (Exception e)
{
this.logger.LogError(e.Message);
throw new RpcException(new Status(StatusCode.Internal, e.Message));
}
}

public override async Task<DeleteAuthorResponse>
DeleteAuthor(DeleteAuthorRequest request, ServerCallContext callContext)
{
var author = await this.context.Authors.FindAsync(new object[] {request.Id}, callContext.CancellationToken);
if (author == null)
throw new RpcException(new Status(StatusCode.NotFound, $"There is no author with id {request.Id}"));

this.context.Authors.Remove(author);
await this.context.SaveChangesAsync(callContext.CancellationToken);

return new DeleteAuthorResponse();
}

public override async Task<UpdateAuthorBooksCountResponse> UpdateAuthorBooksCount(
UpdateAuthorBooksCountRequest request, ServerCallContext callContext)
{
var author = await this.context.Authors.FindAsync(new object[] { request.Id }, callContext.CancellationToken);
if (author == null)
throw new RpcException(new Status(StatusCode.NotFound, $"There is no author with id {request.Id}"));

switch (request.UpdateType)
{
case UpdateType.Increase:
author.NumberOfBooks += request.Delta;
break;
case UpdateType.Decrease:
if (author.NumberOfBooks < request.Delta)
throw new RpcException(new Status(StatusCode.InvalidArgument, $"Author has {author.NumberOfBooks} books but delta is {request.Delta}"));
author.NumberOfBooks -= request.Delta;
break;
default:
throw new RpcException(new Status(StatusCode.InvalidArgument, $"Invalid update type {request.UpdateType}"));
}

this.context.Entry(author).State = EntityState.Modified;

try
{
await this.context.SaveChangesAsync(callContext.CancellationToken);
}
catch (DbUpdateConcurrencyException)
{
if (!this.context.Authors.Any(a => a.Id.ToString() == request.Id))
{
throw new RpcException(new Status(StatusCode.NotFound,
$"There is no author with id {request.Id}"));
}

throw new RpcException(new Status(StatusCode.Internal, "Couldn't update author"));
}

return new UpdateAuthorBooksCountResponse();
}
}
}
5 changes: 3 additions & 2 deletions AuthorsService/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
EXPOSE 5000
EXPOSE 5001
EXPOSE 5002

FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
Expand Down
15 changes: 15 additions & 0 deletions AuthorsService/Extensions/AuthorExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using AuthorsService.Models;

using SharedTypes.Api;

using AuthorGrpcDto = AuthorsService.Grpc.Author;

namespace AuthorsService.Extensions
{
public static class AuthorExtensions
Expand All @@ -15,5 +18,17 @@ public static AuthorDto ToDto(this Author author) =>
LastName = author.LastName,
NumberOfBooks = author.NumberOfBooks
};

public static AuthorGrpcDto ToGrpcDto(this Author author) =>
new()
{
Age = author.Age,
Biography = author.Biography,
FirstName = author.FirstName,
Id = author.Id.ToString(),
LastName = author.LastName,
NumberOfBooks = author.NumberOfBooks
};

}
}
2 changes: 1 addition & 1 deletion AuthorsService/Models/Author.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public class Author
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public byte Age { get; set; }
public uint Age { get; set; }
public string Biography { get; set; }
public uint NumberOfBooks { get; set; }
}
Expand Down
Loading