Skip to content

Commit

Permalink
Version 1.0.12
Browse files Browse the repository at this point in the history
More forced-synchronization methods
  • Loading branch information
pbijdens committed Oct 5, 2024
1 parent 0cd7a24 commit 90ae056
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 11 deletions.
43 changes: 43 additions & 0 deletions CentaurScores/Controllers/DeviceController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using CentaurScores.Model;
using CentaurScores.Services;
using Microsoft.AspNetCore.Mvc;

namespace CentaurScores.Controllers
{
[ApiController]
[Route("/devices")]
public class DeviceController
{
private readonly IMatchRepository matchRepository;

public DeviceController(IMatchRepository matchRepository)
{
this.matchRepository = matchRepository;
}

/// <summary>
/// Returns all pre-defined participants lists for this organization.
/// </summary>
/// <returns></returns>
[HttpGet("{deviceId}/sync")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<bool>> GetDeviceNeedsSync([FromRoute] string deviceId)
{
return await matchRepository.CheckDeviceSynchronization(deviceId);
}

/// <summary>
/// Returns all pre-defined participants lists for this organization.
/// </summary>
/// <returns></returns>
[HttpDelete("{deviceId}/sync")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<bool>> DeleteDeviceNeedsSync([FromRoute] string deviceId)
{
await matchRepository.ClearDeviceSynchronization(deviceId);
return true;
}
}
}
2 changes: 1 addition & 1 deletion CentaurScores/Controllers/MatchesController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public async Task<ActionResult<MatchResultModel>> GetMatchResults([FromRoute] in
}

/// <summary>
/// Returns a single participants list.
/// Clears the remotely changed flag, which indicates
/// </summary>
/// <returns></returns>
[HttpDelete("{id}/remotelychanged")]
Expand Down
1 change: 1 addition & 0 deletions CentaurScores/Persistence/CsSetting.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
public class CsSetting
{
public const string ActiveMatchId = nameof(ActiveMatchId);
public const string DevicesNeedingForcedSync = nameof(DevicesNeedingForcedSync);

required public string Name { get; set; }
public string? JsonValue { get; set; }
Expand Down
2 changes: 2 additions & 0 deletions CentaurScores/Services/IMatchRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ namespace CentaurScores.Services
public interface IMatchRepository
{
Task<MatchModel> ActivateMatch(int id, bool isActive);
Task<bool> CheckDeviceSynchronization(string deviceId);
Task ClearDeviceSynchronization(string deviceId);
Task ClearRemotelyChangedFlag(int matchId);
Task<MatchModel> CreateMatch(MatchModel match);
Task<ParticipantModel> CreateParticipantForMatch(int id, ParticipantModel participant);
Expand Down
73 changes: 64 additions & 9 deletions CentaurScores/Services/MatchRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,11 @@ public async Task<bool> TransferParticipantForMatchToDevice(int id, int particip
throw new InvalidOperationException($"TransferParticipantForMatchToDevice invoked for non-existing participant {participantId}");
}

if (!string.IsNullOrWhiteSpace(participant.DeviceID) && participant.DeviceID != Guid.Empty.ToString())
{
await RequestDeviceSynchronization(db, participant.DeviceID);
}

// If there is currently someone configured for this lijn, remove that record
ParticipantEntity? existingParticipant = db.Participants.Where(x => x.Match.Id == id && x.DeviceID == targetDeviceID && x.Lijn == lijn).FirstOrDefault();
if (null != existingParticipant && existingParticipant.Id != participantId)
Expand All @@ -382,6 +387,8 @@ public async Task<bool> TransferParticipantForMatchToDevice(int id, int particip
participant.DeviceID = targetDeviceID;
participant.Lijn = lijn;

await RequestDeviceSynchronization(db, targetDeviceID);

await db.SaveChangesAsync();

return true;
Expand Down Expand Up @@ -435,14 +442,7 @@ public async Task<ParticipantModel> UpdateParticipantForMatch(int id, int partic
AutoFixParticipantModel(matchModel, participant);
participantEntity.UpdateFromModel(participant);

#if false
// Remove this participant from the device that's managing it because we made changes
// elsewhere. This should ensure the participant
participantEntity.DeviceID = string.Empty;
#endif
// Instead of clearing the device ID, we mark the match as having changed remotely, causing a
// reload on sync.
matchEntity.ChangedRemotely = true;
await RequestDeviceSynchronization(db, participant.DeviceID);

await db.SaveChangesAsync();

Expand All @@ -459,6 +459,10 @@ public async Task<int> DeleteParticipantForMatch(int id, int participantId)
ParticipantEntity? foundEntity = await db.Participants.FirstOrDefaultAsync(x => x.Id == participantId);
if (null != foundEntity)
{
if (!string.IsNullOrWhiteSpace(foundEntity.DeviceID) && foundEntity.DeviceID != Guid.Empty.ToString())
{
await RequestDeviceSynchronization(db, foundEntity.DeviceID);
}
db.Participants.Remove(foundEntity);
await db.SaveChangesAsync();
return 1;
Expand All @@ -469,7 +473,7 @@ public async Task<int> DeleteParticipantForMatch(int id, int participantId)

public async Task<ParticipantModel> CreateParticipantForMatch(int id, ParticipantModel participantModel)
{
using (var db = new CentaurScoresDbContext(configuration))
using (CentaurScoresDbContext db = new CentaurScoresDbContext(configuration))
{
db.Database.EnsureCreated();

Expand Down Expand Up @@ -509,5 +513,56 @@ public async Task ClearRemotelyChangedFlag(int matchId)
match.ChangedRemotely = false;
}
}

private async Task RequestDeviceSynchronization(CentaurScoresDbContext db, string deviceId)
{
// TODO: Might want to use some form of cross-process synchronization here because multiple clients might requets this at the same time.
CsSetting? setting = await db.Settings.FirstOrDefaultAsync(x => x.Name == CsSetting.DevicesNeedingForcedSync);
if (null == setting)
{
setting = new CsSetting { Name = CsSetting.DevicesNeedingForcedSync, JsonValue = JsonConvert.SerializeObject(new string[] { deviceId }) };
db.Add(setting);
}
else
{
List<string> values = JsonConvert.DeserializeObject<List<string>>(setting.JsonValue ?? "[]") ?? [];
if (!values.Contains(deviceId)) values.Add(deviceId);
setting.JsonValue = JsonConvert.SerializeObject(values);
}
}

public async Task ClearDeviceSynchronization(string deviceId)
{
// TODO: Might want to use some form of cross-process synchronization here because multiple clients might requets this at the same time.
using (var db = new CentaurScoresDbContext(configuration))
{
db.Database.EnsureCreated();

CsSetting? setting = await db.Settings.FirstOrDefaultAsync(x => x.Name == CsSetting.DevicesNeedingForcedSync);
if (null != setting)
{
List<string> values = JsonConvert.DeserializeObject<List<string>>(setting.JsonValue ?? "[]") ?? [];
if (values.Contains(deviceId)) values.RemoveAll(x => x == deviceId);
setting.JsonValue = JsonConvert.SerializeObject(values);
await db.SaveChangesAsync();
}
}
}

public async Task<bool> CheckDeviceSynchronization(string deviceId)
{
using (var db = new CentaurScoresDbContext(configuration))
{
db.Database.EnsureCreated();

CsSetting? setting = await db.Settings.AsNoTracking().FirstOrDefaultAsync(x => x.Name == CsSetting.DevicesNeedingForcedSync);
if (null != setting)
{
List<string> values = JsonConvert.DeserializeObject<List<string>>(setting.JsonValue ?? "[]") ?? [];
return values.Contains(deviceId);
}
}
return false;
}
}
}
2 changes: 1 addition & 1 deletion CentaurScores/version.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"version": "1.0.11"
"version": "1.0.12"
}

0 comments on commit 90ae056

Please sign in to comment.