Files
eqnfiresend/Eqn.Sender/Controllers/fbmsController.cs
T
2024-05-16 14:42:09 +02:00

305 lines
12 KiB
C#

using Eqn.Sender.Services;
using FirebaseAdmin;
using FirebaseAdmin.Messaging;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Text;
namespace Eqn.Sender.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class fbmsController : ControllerBase
{
#region Public Constructors
public fbmsController(ILogger<fbmsController> logger, FireBaseService sharedService)
{
_logger = logger;
fbService = sharedService;
}
#endregion Public Constructors
#region Public Methods
[HttpGet("GetInfo")]
public async Task<string> GetInfo()
{
var currProg = typeof(Program).Assembly.GetName();
string answ = $"{currProg.Name} | v.{currProg.Version ?? new Version("0.0.0")}";
//_logger.LogInformation(answ);
await Task.Delay(1);
// restituisco esito...
return answ;
}
#if false
[HttpGet("SearchNum/{id}")]
public async Task<FirebaseAdmin.Auth.UserRecord> SearchNum(string id)
{
FirebaseAdmin.Auth.UserRecord answ = await fbService.GetUserByNumber(id);
await Task.Delay(1);
// restituisco esito...
return answ;
}
[HttpGet("SearchEmail/{id}")]
public async Task<FirebaseAdmin.Auth.UserRecord> SearchEmail(string id)
{
FirebaseAdmin.Auth.UserRecord answ = await fbService.GetUserByEmail(id);
await Task.Delay(1);
// restituisco esito...
return answ;
}
#endif
[HttpPost("sendAndroid")]
public async Task<string> SendAndroid([FromBody] MsgPayload rawData, bool fullReturn = false, int maxToken = 499, int numParall = 4, bool dryRun = false)
{
string answ = await DoSend(rawData, fullReturn, true, maxToken, numParall, dryRun);
// restituisco esito...
return answ;
}
[HttpPost("sendIOS")]
public async Task<string> SendIOS([FromBody] MsgPayload rawData, bool fullReturn = false, int maxToken = 499, int numParall = 4, bool dryRun = false)
{
string answ = await DoSend(rawData, fullReturn, false, maxToken, numParall, dryRun);
// restituisco esito...
return answ;
}
[HttpPost("test")]
public async Task<string> Test(int numTest = 10, int numParall = 1, bool dryRun = true)
{
string answ = "";
StringBuilder sb = new StringBuilder();
List<string> tokenList = new List<string>();
int numOk = 0;
int numErr = 0;
// preparare vera lista invio (multi messaggio)
List<MulticastMessage> messageList = new List<MulticastMessage>();
MulticastMessage newMessage = new MulticastMessage();
tokenList.Add("dzsSHj7kT9yRNFbLUEiLyE:APA91bHLBeSoff8okQG-Z6ZVc2e8xan72z2xOietApLfc7X7XDxhWKAb032miZ5sRGmmSy1QU8MsYzFasEebW_k05piXxDLqfBzpy0vvkr8KY_1Y_BeNB4H0JxIWOxbZHcsEzuK_zVoC");
tokenList.Add("f4-u_BKiTUalvB6Ly2PXvK:APA91bEqp0Hmlfu6D6AazXOLHaEZJBpbDlc8Ss6nBO_PgkxNZ6u0jt9kra6bb7lOVaS2YA_JRYIx-7hWznH_8xfOVLW2jz5a4g8Bh-whPlc3uSFUpW1jYkUYNqr186Ylybw4nIh1kqth");
for (int i = 0; i < numTest; i++)
{
newMessage = new MulticastMessage()
{
Notification = new Notification()
{
Title = "Prova Notifica Firebase",
Body = $"Notifica {i} | dotNet core | {DateTime.Now:yyyy.MM.dd HH:mm:ss}"
}
,
Tokens = tokenList
//,Data =
};
messageList.Add(newMessage);
}
List<BatchResponse> listBResp = new List<BatchResponse>();
Stopwatch sw = new Stopwatch();
sw.Start();
if (numParall > 1)
{
var options = new ParallelOptions { MaxDegreeOfParallelism = numParall };
await Parallel.ForEachAsync(messageList, async (message, token) =>
{
var resp = await fbService.DoSendMessage(message, dryRun);
listBResp.Add(resp);
});
}
else
{
foreach (var item in messageList)
{
var resp = await fbService.DoSendMessage(item, dryRun);
listBResp.Add(resp);
}
}
sw.Stop();
// compongo risultato:
sb.AppendLine($"Effettuate {numTest} chiamate in {sw.Elapsed.TotalSeconds:N3} msec");
numOk = listBResp.Sum(x => x.SuccessCount);
numErr = listBResp.Sum(x => x.FailureCount);
sb.AppendLine($"# Successi: {numOk} | # Errori: {numErr}");
answ = sb.ToString();
// restituisco esito...
return answ;
}
#endregion Public Methods
#region Private Fields
private readonly ILogger<fbmsController> _logger;
#endregion Private Fields
#region Private Properties
private FireBaseService fbService { get; set; } = null!;
#endregion Private Properties
#region Private Methods
/// <summary>
/// Funzione effettiva di invio messaggi spacchettati
/// </summary>
/// <param name="rawData">Dati payload</param>
/// <param name="fullReturn">Indica se ritornare per intero il messaggio firebase</param>
/// <param name="isAndroid">Booleano: true = Android / false=iOS</param>
/// <param name="maxToken">Num max di token x singolo invio FireBase</param>
/// <param name="numParall">Parallelismo send ammesso</param>
/// <param name="dryRun">Se true fa solo SIMULAZIONE invio</param>
/// <returns></returns>
private async Task<string> DoSend(MsgPayload rawData, bool fullReturn, bool isAndroid, int maxToken, int numParall, bool dryRun)
{
string answ = "";
//limito a 499 se anche me ne richiedesse di +...
maxToken = maxToken <= 499 ? maxToken : 499;
Stopwatch sw = new Stopwatch();
sw.Start();
StringBuilder sb = new StringBuilder();
int numOk = 0;
int numErr = 0;
int numResp = 0;
// preparo statistiche invio
ConcurrentBag<double> elapsedList = new ConcurrentBag<double>();
// preparare vera lista invio (multi messaggio)
List<MulticastMessage> messageList = new List<MulticastMessage>();
MulticastMessage newMessage = new MulticastMessage();
// calcolo num invii
int numToken = rawData.tokenList.Count;
int numCall = (int)Math.Ceiling(((double)numToken / maxToken));
// costruisco messaggi con maxToken ognuno
for (int i = 0; i < numCall; i++)
{
// num mess da inviare...
int num2send = numToken - (i * maxToken) > maxToken ? maxToken : numToken - (i * maxToken);
if (isAndroid)
{
newMessage = new MulticastMessage()
{
Android = new AndroidConfig()
{
Priority = rawData.highPriority ? Priority.High : Priority.Normal,
TimeToLive = TimeSpan.FromSeconds(rawData.secTTL)
},
Tokens = rawData.tokenList.Skip(i * maxToken).Take(num2send).ToList(),
Data = rawData.varData
};
}
else
{
Dictionary<string, string> apnsHead = new Dictionary<string, string>();
apnsHead.Add("apns-priority", rawData.highPriority ? "10" : "5");
// expiration come epoch unix:
Int32 unixEpoch = (int)DateTime.UtcNow.AddSeconds(rawData.secTTL).Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
apnsHead.Add("apns-expiration", $"{unixEpoch}");
newMessage = new MulticastMessage()
{
Apns = new ApnsConfig()
{
Headers = apnsHead,
Aps = new Aps()
{
MutableContent = true,
ContentAvailable = true,
Sound = rawData.notifySound,
Alert = new ApsAlert()
{
ActionLocKey = rawData.iOSAction,
TitleLocKey = rawData.title,
LocKey = rawData.body,
LocArgs = new List<string> { rawData.iOSLockArg }
}
},
},
Notification = new Notification()
{
Title = rawData.title,
Body = rawData.body
},
Tokens = rawData.tokenList.Skip(i * maxToken).Take(num2send).ToList(),
Data = rawData.varData
};
}
messageList.Add(newMessage);
}
List<BatchResponse> listBResp = new List<BatchResponse>();
if (numParall > 1)
{
var options = new ParallelOptions { MaxDegreeOfParallelism = numParall };
await Parallel.ForEachAsync(messageList, async (message, token) =>
{
Stopwatch lsw = new Stopwatch();
lsw.Start();
var resp = await fbService.DoSendMessage(message, dryRun);
listBResp.Add(resp);
lsw.Stop();
elapsedList.Add(lsw.Elapsed.TotalSeconds);
});
}
else
{
foreach (var item in messageList)
{
Stopwatch lsw = new Stopwatch();
lsw.Start();
var resp = await fbService.DoSendMessage(item, dryRun);
listBResp.Add(resp);
lsw.Stop();
elapsedList.Add(lsw.Elapsed.TotalSeconds);
}
}
sw.Stop();
// compongo risultato: se riechiesto serializzo risposte firebase...
if (fullReturn)
{
answ = JsonConvert.SerializeObject(listBResp, Formatting.Indented);
}
else
{
// altrimenti solo risultato x conteggio
numOk = listBResp.Sum(x => x.SuccessCount);
numErr = listBResp.Sum(x => x.FailureCount);
numResp = listBResp.Sum(x => x.Responses.Count);
sb.AppendLine($"Effettuate {numCall} chiamate in {sw.Elapsed.TotalSeconds:N3} sec");
sb.AppendLine($"Token IN: {rawData.tokenList.Count} | # Risposte {numResp} | # Successi: {numOk} | # Errori: {numErr}");
answ = sb.ToString();
}
// se durata > 5sec --> loggo!
if (sw.Elapsed.TotalSeconds > 5)
{
int numSend = elapsedList.Count;
string logVal = $"Warnng soglia, durata: {sw.Elapsed.TotalSeconds:N3} sec | chiamate: {numCall} | batch: {numSend}";
_logger.LogWarning(logVal);
// scrivo tutte le righe
int idx = 0;
foreach (var item in elapsedList)
{
_logger.LogWarning($"{idx:00}| {item:N3} sec");
}
//// calcolo numero esecuzioni
//double minTime = elapsedList.OrderBy(x => x).FirstOrDefault();
//double avgTime = numSend > 0 ? elapsedList.Sum(x => x) / numSend : 0;
//double maxTime = elapsedList.OrderByDescending(x => x).FirstOrDefault();
//string stats = $"Batch processati: {numSend} | minTime: {minTime:N3} | avgTime: {avgTime:N3} | maxTime: {maxTime:N3}";
//_logger.LogWarning(stats);
}
return answ;
}
#endregion Private Methods
}
}