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 { /// /// Service der die Verwaltung von Walker-Profilen ermöglicht /// public class WalkerProfileService : ServiceBase, IWalkerProfileService { private readonly ICommunicationService _communicationService; private readonly ISyncInfoPushService _pushService; private readonly ISyncInfoPullService _pullService; /// /// Erstellt eine Instanz /// /// Instanz eines IUnitOfWork /// Instanz eines ICommunicationService /// Instanz eines ISyncInfoPushService /// Instanz eines ISyncInfoPullService public WalkerProfileService(IUnitOfWork unitOfWork, ICommunicationService communicationService, ISyncInfoPushService pushService, ISyncInfoPullService pullService) : base(unitOfWork) { _communicationService = communicationService; _pushService = pushService; _pullService = pullService; } /// /// Gibt eine Entität anhand der eindeutigen Id zurück /// /// Id der Entität /// Gibt an ob NoTRacking verwendet werden soll. Es werden keine Entitäten im EF-Speicher gehalten /// Entität oder null, wenn nicht gefunden public override DogWalkerProfile Get(object id, bool noTracking = true) { //Bewusst nicht implementiert throw new NotImplementedException(); } /// /// Gibt eine Entität anhand der eindeutigen Id zurück /// /// Id der Entität /// Gibt an ob NoTRacking verwendet werden soll. Es werden keine Entitäten im EF-Speicher gehalten /// Entität oder null, wenn nicht gefunden public override async Task GetAsync(object id, bool noTracking = true) { //Bewusst nicht implementiert await Task.Delay(1); throw new NotImplementedException(); } /// /// Gibt das aktuelle Walkerprofil zurück. /// /// DogWalkerProfile oder null wenn nicht gefunden public async Task GetAsync() { var profile = await Repository.FirstOrDefaultAsync(c => c.Id != "", false).ConfigureAwait(false); return profile; } /// /// Gibt das aktuelle Walkerprofil zurück. /// Versucht auch online - wenn lokal nicht vorhanden /// /// Aktuelles Accesstoken /// CancellationToken /// Wenn true, wird in jedem Fall die Online-Abfrage verwendet /// DogWalkerProfile oder null wenn nicht gefunden public async Task 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; } /// /// Hinzufügen oder aktualisieren eines Walkerprofils /// /// Walker-Profil /// AppUser public async Task 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(); } /// /// Aktualisieren eines Walker-Profils. /// Online wird versucht. Wenn nicht möglich wird ein Delta erstellt. /// /// Walker-Profil /// Aktuelles Accesstoken /// CancellationToken /// DogWalkerProfile public async Task 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; } /// /// Holen der Daten vom lokalen Speicher die noch nicht synchronisiert wurden und senden an den Server /// /// Aktuelles Accesstoken /// CancellationToken /// Task public async Task> PushAsync(string accessToken, CancellationToken token) { var result = new SyncResult(); 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(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; } /// /// Holen der letzten Daten vom Server und Synchronisieren mit den lokalen Daten /// /// Sprache /// Aktuelles Accesstoken /// CancellationToken /// Task public async Task> PullAsync(string language, string accessToken, CancellationToken token) { var result = new SyncResult(); //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; } } }