// ------------------------------------------------------------------------ // Copyright 2021 The Dapr Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ------------------------------------------------------------------------ using System; using System.Text.Json; using System.Threading.Tasks; using Dapr.AppCallback.Autogen.Grpc.v1; using Dapr.Client; using Dapr.Client.Autogen.Grpc.v1; using Google.Protobuf.WellKnownTypes; using Grpc.Core; using GrpcServiceSample.Generated; using Microsoft.Extensions.Logging; namespace GrpcServiceSample.Services { /// /// BankAccount gRPC service /// public class BankingService : AppCallback.AppCallbackBase { /// /// State store name. /// public const string StoreName = "statestore"; private readonly ILogger _logger; private readonly DaprClient _daprClient; /// /// Constructor /// /// /// public BankingService(DaprClient daprClient, ILogger logger) { _daprClient = daprClient; _logger = logger; } readonly JsonSerializerOptions jsonOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; /// /// implement OnIvoke to support getaccount, deposit and withdraw /// /// /// /// public override async Task OnInvoke(InvokeRequest request, ServerCallContext context) { var response = new InvokeResponse(); switch (request.Method) { case "getaccount": var input = request.Data.Unpack(); var output = await GetAccount(input, context); response.Data = Any.Pack(output); break; case "deposit": case "withdraw": var transaction = request.Data.Unpack(); var account = request.Method == "deposit" ? await Deposit(transaction, context) : await Withdraw(transaction, context); response.Data = Any.Pack(account); break; default: break; } return response; } /// /// implement ListTopicSubscriptions to register deposit and withdraw subscriber /// /// /// /// public override Task ListTopicSubscriptions(Empty request, ServerCallContext context) { var result = new ListTopicSubscriptionsResponse(); result.Subscriptions.Add(new TopicSubscription { PubsubName = "pubsub", Topic = "deposit" }); result.Subscriptions.Add(new TopicSubscription { PubsubName = "pubsub", Topic = "withdraw" }); return Task.FromResult(result); } /// /// implement OnTopicEvent to handle deposit and withdraw event /// /// /// /// public override async Task OnTopicEvent(TopicEventRequest request, ServerCallContext context) { if (request.PubsubName == "pubsub") { var input = JsonSerializer.Deserialize(request.Data.ToStringUtf8(), this.jsonOptions); var transaction = new GrpcServiceSample.Generated.Transaction() { Id = input.Id, Amount = (int)input.Amount, }; if (request.Topic == "deposit") { await Deposit(transaction, context); } else { await Withdraw(transaction, context); } } return new TopicEventResponse(); } /// /// GetAccount /// /// /// /// public async Task GetAccount(GetAccountRequest input, ServerCallContext context) { var state = await _daprClient.GetStateEntryAsync(StoreName, input.Id); return new GrpcServiceSample.Generated.Account() { Id = state.Value.Id, Balance = (int)state.Value.Balance, }; } /// /// Deposit /// /// /// /// public async Task Deposit(GrpcServiceSample.Generated.Transaction transaction, ServerCallContext context) { _logger.LogDebug("Enter deposit"); var state = await _daprClient.GetStateEntryAsync(StoreName, transaction.Id); state.Value ??= new Models.Account() { Id = transaction.Id, }; state.Value.Balance += transaction.Amount; await state.SaveAsync(); return new GrpcServiceSample.Generated.Account() { Id = state.Value.Id, Balance = (int)state.Value.Balance, }; } /// /// Withdraw /// /// /// /// public async Task Withdraw(GrpcServiceSample.Generated.Transaction transaction, ServerCallContext context) { _logger.LogDebug("Enter withdraw"); var state = await _daprClient.GetStateEntryAsync(StoreName, transaction.Id); if (state.Value == null) { throw new Exception($"NotFound: {transaction.Id}"); } state.Value.Balance -= transaction.Amount; await state.SaveAsync(); return new GrpcServiceSample.Generated.Account() { Id = state.Value.Id, Balance = (int)state.Value.Balance, }; } } }