Initial version
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
using EonaCat.gRPC.Proto;
|
||||
|
||||
namespace EonaCat.gRPC.Client;
|
||||
|
||||
public interface ITokenProvider
|
||||
{
|
||||
Task<AuthenticationResponse> GetTokenAsync();
|
||||
}
|
||||
|
||||
public class AppTokenProvider : ITokenProvider
|
||||
{
|
||||
private AuthenticationResponse _token;
|
||||
public async Task<AuthenticationResponse> GetTokenAsync()
|
||||
{
|
||||
if (_token == null)
|
||||
_token = new AuthenticationResponse { AccessToken = "test", ExpiresIn = 300 };
|
||||
return _token;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using Grpc.Core;
|
||||
using ProtoBuf.Grpc.Client;
|
||||
using EonaCat.gRPC.Proto;
|
||||
|
||||
namespace EonaCat.gRPC.Client;
|
||||
|
||||
public class AuthService
|
||||
{
|
||||
public static async Task Authenticate(CallInvoker invoker)
|
||||
{
|
||||
var authenticationClient = invoker.CreateGrpcService<IAuthenticationService>();
|
||||
var authenticationResponse = await authenticationClient.Authenticate(new AuthenticationRequest
|
||||
{
|
||||
UserName = "admin",
|
||||
Password = "admin"
|
||||
});
|
||||
Console.WriteLine($"Received Authentication Response - \nToken: {authenticationResponse.AccessToken}\nExpires In: {authenticationResponse.ExpiresIn}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Interfaces\**" />
|
||||
<EmbeddedResource Remove="Interfaces\**" />
|
||||
<None Remove="Interfaces\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Protos\base.proto" />
|
||||
<None Remove="Protos\user.proto" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Bogus" Version="35.6.5" />
|
||||
<PackageReference Include="EonaCat.LogStack" Version="0.0.8" />
|
||||
<PackageReference Include="Grpc.Net.Client" Version="2.80.0" />
|
||||
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.80.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\EonaCat.gRPC.Proto\EonaCat.gRPC.Proto.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Protobuf Include="Protos\base.proto" GrpcServices="Client" />
|
||||
<Protobuf Include="Protos\user.proto" GrpcServices="Client" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,101 @@
|
||||
using Bogus;
|
||||
using Grpc.Core;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ProtoBuf.Grpc.Client;
|
||||
using EonaCat.gRPC.Client.Helpers;
|
||||
using EonaCat.gRPC.Proto;
|
||||
using EonaCat.LogStack.Logging;
|
||||
|
||||
namespace EonaCat.gRPC.Client;
|
||||
|
||||
public static class Extension
|
||||
{
|
||||
public static async Task ExecutePrograms(CallInvoker callInvoker)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
Console.WriteLine("\nEnter 1 to execute Authenticate.\n" +
|
||||
"Enter 2 to execute Create User With Demo Data\n" +
|
||||
"Enter 3 to execute User List Async\n" +
|
||||
"Enter 4 to execute User GetById Async\n" +
|
||||
"Enter 0 to exit.\n");
|
||||
|
||||
var input = Console.ReadLine();
|
||||
input = input?.Trim(' ', '"', '\'');
|
||||
|
||||
if (!int.TryParse(input, out var value) || value is not (0 or 1 or 2 or 3 or 4))
|
||||
{
|
||||
ConsoleExtensions.Error("Invalid input. Please enter a valid option (0, 1, 2, 3, or 4).");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value == 0)
|
||||
break;
|
||||
|
||||
try
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case 1:
|
||||
await AuthService.Authenticate(callInvoker);
|
||||
break;
|
||||
case 2:
|
||||
await CreateUser(callInvoker);
|
||||
break;
|
||||
case 3:
|
||||
await UserListAsync(callInvoker);
|
||||
break;
|
||||
case 4:
|
||||
await UserGetByIdAsync(callInvoker);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ConsoleExtensions.Error($"Error: {e.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task Authenticate(CallInvoker invoker)
|
||||
{
|
||||
var authenticationClient = invoker.CreateGrpcService<IAuthenticationService>();
|
||||
var authenticationResponse = await authenticationClient.Authenticate(new AuthenticationRequest
|
||||
{
|
||||
UserName = "admin",
|
||||
Password = "admin"
|
||||
});
|
||||
ConsoleExtensions.Success($"Received Authentication Response - \nToken: {authenticationResponse.AccessToken}\nExpires In: {authenticationResponse.ExpiresIn}");
|
||||
}
|
||||
|
||||
private static async Task CreateUser(CallInvoker callInvoker)
|
||||
{
|
||||
var userClient = callInvoker.CreateGrpcService<IProtoUserService>();
|
||||
var faker = new Faker();
|
||||
var userCreateRequest = new UserCreateRequest
|
||||
{
|
||||
FirstName = faker.Person.FirstName,
|
||||
LastName = faker.Person.LastName,
|
||||
Email = faker.Person.Email
|
||||
};
|
||||
var userResponse = await userClient.Create(userCreateRequest);
|
||||
ConsoleExtensions.PrintResponse(userResponse);
|
||||
}
|
||||
|
||||
private static async Task UserGetByIdAsync(CallInvoker callInvoker)
|
||||
{
|
||||
Console.Write("Please Enter an UserId : ");
|
||||
var userId = Console.ReadLine() ?? string.Empty;
|
||||
userId = userId.Trim(' ', '"', '\'');
|
||||
var userClient = callInvoker.CreateGrpcService<IProtoUserService>();
|
||||
var userResponse = await userClient.GetByIdAsync(userId);
|
||||
ConsoleExtensions.PrintResponse(userResponse);
|
||||
}
|
||||
|
||||
private static async Task UserListAsync(CallInvoker callInvoker)
|
||||
{
|
||||
var userClient = callInvoker.CreateGrpcService<IProtoUserService>();
|
||||
var userResponse = await userClient.GetAsync();
|
||||
ConsoleExtensions.PrintResponse(userResponse);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using Grpc.Core;
|
||||
using Grpc.Core.Interceptors;
|
||||
using EonaCat.gRPC.Proto;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace EonaCat.gRPC.Client.Helpers;
|
||||
|
||||
public class AuthHeaderInterceptor : Interceptor
|
||||
{
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
public AuthHeaderInterceptor(IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
|
||||
public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context,
|
||||
AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
|
||||
{
|
||||
var metadata = new Metadata();
|
||||
//var authResponse = AuthService.Authenticate();
|
||||
var authResponse = new AuthenticationResponse();
|
||||
metadata.Add("authorization", $"Bearer {authResponse.AccessToken}");
|
||||
metadata.Add("expiry", $"{authResponse.ExpiresIn}");
|
||||
|
||||
var userIdentity = _httpContextAccessor.HttpContext?.User.Identity;
|
||||
if (userIdentity is { IsAuthenticated: true, Name: { } })
|
||||
metadata.Add("User", userIdentity.Name);
|
||||
|
||||
var callOptions = context.Options.WithHeaders(metadata);
|
||||
context = new ClientInterceptorContext<TRequest, TResponse>(context.Method, context.Host, callOptions);
|
||||
|
||||
return base.AsyncUnaryCall(request, context, continuation);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using EonaCat.gRPC.Proto;
|
||||
using EonaCat.Json;
|
||||
|
||||
namespace EonaCat.gRPC.Client.Helpers;
|
||||
|
||||
public static class ConsoleExtensions
|
||||
{
|
||||
public static void Success(string data, bool disableNewLine = false)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
if (disableNewLine)
|
||||
Console.Write(data);
|
||||
else
|
||||
Console.WriteLine(data);
|
||||
Console.ResetColor();
|
||||
}
|
||||
|
||||
public static void Error(string message, bool disableNewLine = false)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
if (disableNewLine)
|
||||
Console.Write(message);
|
||||
else
|
||||
Console.WriteLine(message);
|
||||
Console.ResetColor();
|
||||
}
|
||||
|
||||
public static void PrintResponse<T>(BaseResponse<T> response, Formatting formatting = Formatting.Indented) where T : class?
|
||||
{
|
||||
try
|
||||
{
|
||||
if (response.IsSuccess)
|
||||
{
|
||||
Success("------ Success Response ------");
|
||||
if (response.Data != null)
|
||||
{
|
||||
Success("Response:");
|
||||
Success(JsonHelper.ToJson(response, formatting));
|
||||
foreach (var property in response.Data.GetType().GetProperties())
|
||||
{
|
||||
var value = property.GetValue(response.Data);
|
||||
if (value != null)
|
||||
{
|
||||
Success($"{property.Name}: {value}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Success($"{property.Name}: null");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Success("No data available in the response.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Print error response
|
||||
Error("------ Error Response ------");
|
||||
Error($"Message: {response.Message}");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// do nothing.
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// enhance-your-grpc-client-logs-with-a-generic-logging-interceptor
|
||||
// https://anthonygiretti.com/2022/08/08/net-6-enhance-your-grpc-client-logs-with-a-generic-logging-interceptor/
|
||||
|
||||
using Grpc.Core;
|
||||
using Grpc.Core.Interceptors;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace EonaCat.gRPC.Client.Helpers;
|
||||
|
||||
public class TracerInterceptor : Interceptor
|
||||
{
|
||||
private readonly ILogger<TracerInterceptor> _logger;
|
||||
|
||||
public TracerInterceptor(ILoggerFactory logger)
|
||||
{
|
||||
_logger = logger.CreateLogger<TracerInterceptor>();
|
||||
}
|
||||
|
||||
public override AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(
|
||||
ClientInterceptorContext<TRequest, TResponse> context,
|
||||
AsyncClientStreamingCallContinuation<TRequest, TResponse> continuation)
|
||||
where TRequest : class
|
||||
where TResponse : class
|
||||
{
|
||||
_logger.LogDebug($"Calling {context.Method.Name} {context.Method.Type} method at {DateTime.UtcNow} UTC from machine {Environment.MachineName}");
|
||||
var continued = continuation(context);
|
||||
|
||||
return continued;
|
||||
}
|
||||
|
||||
public override AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(
|
||||
ClientInterceptorContext<TRequest, TResponse> context,
|
||||
AsyncDuplexStreamingCallContinuation<TRequest, TResponse> continuation)
|
||||
where TRequest : class
|
||||
where TResponse : class
|
||||
{
|
||||
_logger.LogDebug($"Calling {context.Method.Name} {context.Method.Type} method at {DateTime.UtcNow} UTC from machine {Environment.MachineName}");
|
||||
var continued = continuation(context);
|
||||
|
||||
return continued;
|
||||
}
|
||||
|
||||
public override AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(
|
||||
TRequest request,
|
||||
ClientInterceptorContext<TRequest, TResponse> context,
|
||||
AsyncServerStreamingCallContinuation<TRequest, TResponse> continuation)
|
||||
where TRequest : class
|
||||
where TResponse : class
|
||||
{
|
||||
_logger.LogDebug($"Calling {context.Method.Name} {context.Method.Type} method. Payload received: {request.GetType()} : {request}");
|
||||
_logger.LogDebug($"Calling {context.Method.Name} {context.Method.Type} method at {DateTime.UtcNow} UTC from machine {Environment.MachineName}");
|
||||
var continued = continuation(request, context);
|
||||
|
||||
return continued;
|
||||
}
|
||||
|
||||
public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
|
||||
TRequest request,
|
||||
ClientInterceptorContext<TRequest, TResponse> context,
|
||||
AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
|
||||
where TRequest : class
|
||||
where TResponse : class
|
||||
{
|
||||
_logger.LogDebug($"Calling {context.Method.Name} {context.Method.Type} method. Payload received: {request.GetType()} : {request}");
|
||||
_logger.LogDebug($"Calling {context.Method.Name} {context.Method.Type} method at {DateTime.UtcNow} UTC from machine {Environment.MachineName}");
|
||||
var continued = continuation(request, context);
|
||||
|
||||
return continued;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using Grpc.Core.Interceptors;
|
||||
using Grpc.Net.Client;
|
||||
using EonaCat.gRPC.Client.Helpers;
|
||||
using EonaCat.gRPC.Client;
|
||||
using EonaCat.LogStack.Logging;
|
||||
using EonaCat.LogStack.Extensions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
Console.WriteLine("Welcome to EonaCat.gRPC Client Application.");
|
||||
|
||||
const string serverAddress = "http://localhost:5227";
|
||||
|
||||
//var (invoker, channel) = Extension.ConfigureChannel(serverAddress, loggerFactory);
|
||||
|
||||
using var channel = GrpcChannel.ForAddress(serverAddress, new GrpcChannelOptions
|
||||
{
|
||||
LoggerFactory = Microsoft.Extensions.Logging.LoggerFactory.Create(builder =>
|
||||
{
|
||||
builder.AddEonaCatLogging();
|
||||
builder.SetMinimumLevel(LogLevel.Debug);
|
||||
})
|
||||
});
|
||||
|
||||
var invoker = channel.Intercept(new TracerInterceptor(Microsoft.Extensions.Logging.LoggerFactory.Create(builder =>
|
||||
{
|
||||
builder.AddEonaCatLogging();
|
||||
builder.SetMinimumLevel(LogLevel.Debug);
|
||||
})));
|
||||
|
||||
await Extension.ExecutePrograms(invoker);
|
||||
|
||||
Console.ReadKey();
|
||||
await channel.ShutdownAsync();
|
||||
@@ -0,0 +1,10 @@
|
||||
syntax = "proto3";
|
||||
option csharp_namespace = "GRPC.NET7.Api.Protos";
|
||||
package base;
|
||||
|
||||
|
||||
message BaseResponse {
|
||||
bool isSuccess = 1;
|
||||
string message = 2;
|
||||
string data = 3;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
syntax = "proto3";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "google/protobuf/empty.proto";
|
||||
import "Protos/base.proto";
|
||||
|
||||
option csharp_namespace = "GRPC.NET7.Api.Protos";
|
||||
package user;
|
||||
|
||||
service User {
|
||||
rpc Create(UserCreateRequest) returns (base.BaseResponse) { }
|
||||
rpc Get(google.protobuf.Empty) returns (base.BaseResponse) { }
|
||||
}
|
||||
|
||||
message UserCreateRequest {
|
||||
string firstName = 1;
|
||||
string lastName = 2;
|
||||
string email = 3;
|
||||
//google.protobuf.Timestamp dateOfBirth = 4;
|
||||
string gender = 5;
|
||||
}
|
||||
Reference in New Issue
Block a user