343 lines
16 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using gehGassi.Dto;
using gehGassi.Dto.DogWalkers;
using gehGassiApp.Core.Data;
using gehGassiApp.Core.Interfaces;
using gehGassiApp.Core.Interfaces.Synchronization;
using gehGassiApp.Core.Mapper;
using gehGassiApp.Domain.Common;
using gehGassiApp.Domain.Users;
namespace gehGassiApp.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Walker-Profilen ermöglicht
/// </summary>
public class WalkerProfileService : ServiceBase<DogWalkerProfile>, IWalkerProfileService
{
private readonly ICommunicationService _communicationService;
private readonly ISyncInfoPushService _pushService;
private readonly ISyncInfoPullService _pullService;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
/// <param name="communicationService">Instanz eines ICommunicationService</param>
/// <param name="pushService">Instanz eines ISyncInfoPushService</param>
/// <param name="pullService">Instanz eines ISyncInfoPullService</param>
public WalkerProfileService(IUnitOfWork unitOfWork, ICommunicationService communicationService, ISyncInfoPushService pushService, ISyncInfoPullService pullService) : base(unitOfWork)
{
_communicationService = communicationService;
_pushService = pushService;
_pullService = pullService;
}
/// <summary>
/// Gibt eine Entität anhand der eindeutigen Id zurück
/// </summary>
/// <param name="id">Id der Entität</param>
/// <param name="noTracking">Gibt an ob NoTRacking verwendet werden soll. Es werden keine Entitäten im EF-Speicher gehalten</param>
/// <returns>Entität oder null, wenn nicht gefunden</returns>
public override DogWalkerProfile Get(object id, bool noTracking = true)
{
//Bewusst nicht implementiert
throw new NotImplementedException();
}
/// <summary>
/// Gibt eine Entität anhand der eindeutigen Id zurück
/// </summary>
/// <param name="id">Id der Entität</param>
/// <param name="noTracking">Gibt an ob NoTRacking verwendet werden soll. Es werden keine Entitäten im EF-Speicher gehalten</param>
/// <returns>Entität oder null, wenn nicht gefunden</returns>
public override async Task<DogWalkerProfile> GetAsync(object id, bool noTracking = true)
{
//Bewusst nicht implementiert
await Task.Delay(1);
throw new NotImplementedException();
}
/// <summary>
/// Gibt das aktuelle Walkerprofil zurück.
/// </summary>
/// <returns>DogWalkerProfile oder null wenn nicht gefunden</returns>
public async Task<DogWalkerProfile> GetAsync()
{
var profile = await Repository.FirstOrDefaultAsync(c => c.Id != "", false).ConfigureAwait(false);
return profile;
}
/// <summary>
/// Gibt das aktuelle Walkerprofil zurück.
/// Versucht auch online - wenn lokal nicht vorhanden
/// </summary>
/// <param name="accessToken">Aktuelles Accesstoken</param>
/// <param name="token">CancellationToken</param>
/// <param name="forceOnline">Wenn true, wird in jedem Fall die Online-Abfrage verwendet</param>
/// <returns>DogWalkerProfile oder null wenn nicht gefunden</returns>
public async Task<DogWalkerProfile> GetAsync(string accessToken, CancellationToken token, bool forceOnline)
{
//Zuerst Online wenn gewünscht
if (forceOnline)
{
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
var profileResult = await _communicationService.GetWalkerProfileAsync(accessToken, token);
if (profileResult.Success)
{
var profileUpdated = await CreateOrUpdateAsync(profileResult.Value);
await _pullService.AddOrUpddateAsync(nameof(DogWalkerProfile), DateTimeOffset.UtcNow);
return profileUpdated;
}
}
}
var profile = await Repository.FirstOrDefaultAsync(c => c.Id != "").ConfigureAwait(false);
if (profile != null)
return profile;
return null;
}
/// <summary>
/// Hinzufügen oder aktualisieren eines Walkerprofils
/// </summary>
/// <param name="walkerProfile">Walker-Profil</param>
/// <returns>AppUser</returns>
public async Task<DogWalkerProfile> CreateOrUpdateAsync(DogWalkerProfile walkerProfile)
{
var foundProfile = await Repository.FirstOrDefaultAsync(c => c.Id != "", true).ConfigureAwait(false);
if (foundProfile == null)
{
Repository.Add(walkerProfile);
await CommitAsync();
return walkerProfile;
}
if (walkerProfile.UpdatedAt > foundProfile.UpdatedAt)
{
foundProfile.Radius = walkerProfile.Radius;
foundProfile.About = walkerProfile.About;
foundProfile.AllowedRequests = walkerProfile.AllowedRequests;
foundProfile.PuppyAllowed = walkerProfile.PuppyAllowed;
foundProfile.DifficultAllowed = walkerProfile.DifficultAllowed;
foundProfile.AggressionLevel = walkerProfile.AggressionLevel;
foundProfile.AcceptedSize = walkerProfile.AcceptedSize;
foundProfile.PriceWalk = walkerProfile.PriceWalk;
foundProfile.PriceDay = walkerProfile.PriceDay;
foundProfile.PriceSitting = walkerProfile.PriceSitting;
foundProfile.NotAvailable = walkerProfile.NotAvailable;
foundProfile.NotAvailableInfo = walkerProfile.NotAvailableInfo;
foundProfile.UpdatedAt = walkerProfile.UpdatedAt;
foundProfile.Deleted = walkerProfile.Deleted;
Repository.Update(foundProfile);
var changes = await CommitAsync().ConfigureAwait(false);
}
return await GetAsync();
}
/// <summary>
/// Aktualisieren eines Walker-Profils.
/// Online wird versucht. Wenn nicht möglich wird ein Delta erstellt.
/// </summary>
/// <param name="walkerProfile">Walker-Profil</param>
/// <param name="accessToken">Aktuelles Accesstoken</param>
/// <param name="token">CancellationToken</param>
/// <returns>DogWalkerProfile</returns>
public async Task<DogWalkerProfile> UpdateAsync(DogWalkerProfile walkerProfile, string accessToken, CancellationToken token)
{
//Zuerst in der DB aktualisieren
var foundProfile = await Repository.FirstOrDefaultAsync(c => c.Id == walkerProfile.Id, false);
foundProfile.Radius = walkerProfile.Radius;
foundProfile.About = walkerProfile.About;
foundProfile.AllowedRequests = walkerProfile.AllowedRequests;
foundProfile.PuppyAllowed = walkerProfile.PuppyAllowed;
foundProfile.DifficultAllowed = walkerProfile.DifficultAllowed;
foundProfile.AggressionLevel = walkerProfile.AggressionLevel;
foundProfile.AcceptedSize = walkerProfile.AcceptedSize;
foundProfile.PriceWalk = walkerProfile.PriceWalk;
foundProfile.PriceDay = walkerProfile.PriceDay;
foundProfile.PriceSitting = walkerProfile.PriceSitting;
foundProfile.NotAvailable = walkerProfile.NotAvailable;
foundProfile.NotAvailableInfo = walkerProfile.NotAvailableInfo;
foundProfile.UpdatedAt = DateTimeOffset.UtcNow;
Repository.Update(foundProfile);
await CommitAsync().ConfigureAwait(false);
var profileDto = foundProfile.ToDto();
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
var updateResult = await _communicationService.UpdateWalkerProfileAsync(profileDto, accessToken, token);
if (updateResult.Success)
{
//Alles gut nichts weglegen
//Für den Pull-Service das Update-Datum setzen
await _pullService.AddOrUpddateAsync(nameof(DogWalkerProfile), foundProfile.UpdatedAt).ConfigureAwait(false);
}
else
{
//Weglegen und später versuchen
await _pushService.AddAsync(nameof(DogWalkerProfile), foundProfile.Id, SyncOperation.Edit, foundProfile).ConfigureAwait(false);
}
}
else
{
//Weglegen und später versuchen
await _pushService.AddAsync(nameof(DogWalkerProfile), foundProfile.Id, SyncOperation.Edit, foundProfile).ConfigureAwait(false);
}
return foundProfile;
}
/// <summary>
/// Holen der Daten vom lokalen Speicher die noch nicht synchronisiert wurden und senden an den Server
/// </summary>
/// <param name="accessToken">Aktuelles Accesstoken</param>
/// <param name="token">CancellationToken</param>
/// <returns>Task</returns>
public async Task<SyncResult<DogWalkerProfile>> PushAsync(string accessToken, CancellationToken token)
{
var result = new SyncResult<DogWalkerProfile>();
if (await _pushService.HasOpenAsync(nameof(DogWalkerProfile)) > 0)
{
var deltas = await _pushService.GetAllAsync(nameof(DogWalkerProfile)).ConfigureAwait(false);
if (deltas.Any())
{
var isConnected = await _communicationService.IsConnected();
if (!isConnected) return result;
deltas = deltas.OrderBy(c => c.DateTime).ToList();
foreach (var syncInfoPush in deltas)
{
//Es gibt hier ur Update... daher alles andere ignorieren
if (syncInfoPush.Operation != SyncOperation.Edit)
{
await _pushService.RemoveAsync(syncInfoPush.Id).ConfigureAwait(false);
continue;
}
try
{
if (!string.IsNullOrWhiteSpace(syncInfoPush.Value))
{
var profile = JsonSerializer.Deserialize<DogWalkerProfile>(syncInfoPush.Value, new JsonSerializerOptions(JsonSerializerDefaults.Web));
//Jetzt Update senden...
var profileDto = profile.ToDto();
var updateResult = await _communicationService.UpdateWalkerProfileAsync(profileDto, accessToken, token);
if (updateResult.Success)
{
//Aktualisierung durchgeführt.
await _pullService.AddOrUpddateAsync(nameof(DogWalkerProfile), profile.UpdatedAt).ConfigureAwait(false);
await _pushService.RemoveAsync(syncInfoPush.Id).ConfigureAwait(false);
}
else
{
//TODO: Prüfen!!!!
//Derzeiut egal welcher Fehler vorliegt, weg damit
//Aktualisierung durchgeführt.
await _pullService.AddOrUpddateAsync(nameof(DogWalkerProfile), profile.UpdatedAt).ConfigureAwait(false);
await _pushService.RemoveAsync(syncInfoPush.Id).ConfigureAwait(false);
}
//Else ist nichts tun, konnte nicht übertragen werden.
}
else
{
await _pushService.RemoveAsync(syncInfoPush.Id).ConfigureAwait(false);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
await _pushService.RemoveAsync(syncInfoPush.Id).ConfigureAwait(false);
}
}
}
}
//Es gibt hier kein Push!
await Task.Delay(1);
return result;
}
/// <summary>
/// Holen der letzten Daten vom Server und Synchronisieren mit den lokalen Daten
/// </summary>
/// <param name="language">Sprache</param>
/// <param name="accessToken">Aktuelles Accesstoken</param>
/// <param name="token">CancellationToken</param>
/// <returns>Task</returns>
public async Task<SyncResult<DogWalkerProfile>> PullAsync(string language, string accessToken, CancellationToken token)
{
var result = new SyncResult<DogWalkerProfile>();
//Zuerst immer online...
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
//Zuerst lezte Aktivität holen...
DateTimeOffset? lastUpdate = null;
var lastSyncInfo = await _pullService.GetAsync(nameof(DogWalkerProfile)).ConfigureAwait(false);
if (lastSyncInfo != null)
lastUpdate = lastSyncInfo.LastUpdate;
var ctsQuery = new CancellationTokenSource(Common.Constants.PushPullTimeout);
var profileResult = await _communicationService.GetWalkerProfileSyncAsync(lastUpdate, accessToken, token);
if (profileResult.Success)
{
if (profileResult.Value != null)
{
var profile = await Repository.GetAsync(profileResult.Value.Id).ConfigureAwait(false);
if (profile != null)
{
profile.Radius = profileResult.Value.Radius;
profile.About = profileResult.Value.About;
profile.AllowedRequests = profileResult.Value.AllowedRequests;
profile.PuppyAllowed = profileResult.Value.PuppyAllowed;
profile.DifficultAllowed = profileResult.Value.DifficultAllowed;
profile.AggressionLevel = profileResult.Value.AggressionLevel;
profile.AcceptedSize = profileResult.Value.AcceptedSize;
profile.PriceWalk = profileResult.Value.PriceWalk;
profile.PriceDay = profileResult.Value.PriceDay;
profile.PriceSitting = profileResult.Value.PriceSitting;
profile.NotAvailable = profileResult.Value.NotAvailable;
profile.NotAvailableInfo = profileResult.Value.NotAvailableInfo;
profile.UpdatedAt = DateTimeOffset.UtcNow;
Repository.Update(profile);
await CommitAsync().ConfigureAwait(false);
result.Updated.Add(profile);
result.LastUpdate = profileResult.Value.UpdatedAt;
await _pullService.AddOrUpddateAsync(nameof(DogWalkerProfile), profileResult.Value.UpdatedAt).ConfigureAwait(false);
}
}
}
result.LastUpdate ??= lastUpdate;
}
return result;
}
}
}