Skip to content

Commit ab69398

Browse files
renemadsenclaude
andcommitted
refactor: move DeviceToken entity to TimePlanningBase NuGet package
Remove local DeviceToken model and DeviceTokenDbContext — the entity now lives in Microting.TimePlanningBase v10.0.35 with proper EF Core migrations. Update services and tests to use TimePlanningPnDbContext. Read Firebase credentials from PluginConfigurationValues instead of file path. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d42fdfe commit ab69398

File tree

8 files changed

+30
-176
lines changed

8 files changed

+30
-176
lines changed

eFormAPI/Plugins/TimePlanning.Pn/TimePlanning.Pn.Test/DeviceTokenServiceTests.cs

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,26 @@
22
using System.Threading.Tasks;
33
using Microsoft.EntityFrameworkCore;
44
using Microsoft.Extensions.Logging;
5+
using Microting.TimePlanningBase.Infrastructure.Data;
56
using NSubstitute;
67
using NUnit.Framework;
7-
using TimePlanning.Pn.Infrastructure.Models.DeviceToken;
88
using TimePlanning.Pn.Services.DeviceTokenService;
99

1010
namespace TimePlanning.Pn.Test;
1111

1212
[TestFixture]
1313
public class DeviceTokenServiceTests
1414
{
15-
private DeviceTokenDbContext _dbContext = null!;
15+
private TimePlanningPnDbContext _dbContext = null!;
1616
private DeviceTokenService _service = null!;
1717

1818
[SetUp]
1919
public void SetUp()
2020
{
21-
var options = new DbContextOptionsBuilder<DeviceTokenDbContext>()
21+
var options = new DbContextOptionsBuilder<TimePlanningPnDbContext>()
2222
.UseInMemoryDatabase(databaseName: $"DeviceTokenTestDb_{TestContext.CurrentContext.Test.Name}")
2323
.Options;
24-
_dbContext = new DeviceTokenDbContext(options);
24+
_dbContext = new TimePlanningPnDbContext(options);
2525
_service = new DeviceTokenService(
2626
_dbContext,
2727
Substitute.For<ILogger<DeviceTokenService>>());
@@ -37,29 +37,23 @@ public void TearDown()
3737
[Test]
3838
public async Task RegisterAsync_NewToken_IsStored()
3939
{
40-
// Act
4140
var result = await _service.RegisterAsync(42, "fcm-token-abc", "android");
4241

43-
// Assert
4442
Assert.That(result.Success, Is.True);
4543

4644
var stored = await _dbContext.DeviceTokens.SingleAsync();
4745
Assert.That(stored.SdkSiteId, Is.EqualTo(42));
4846
Assert.That(stored.Token, Is.EqualTo("fcm-token-abc"));
4947
Assert.That(stored.Platform, Is.EqualTo("android"));
50-
Assert.That(stored.WorkflowState, Is.EqualTo("created"));
5148
}
5249

5350
[Test]
5451
public async Task RegisterAsync_SameTokenTwice_UpsertsWithoutDuplicate()
5552
{
56-
// Arrange
5753
await _service.RegisterAsync(1, "dup-token", "android");
5854

59-
// Act — re-register same token with different site
6055
var result = await _service.RegisterAsync(2, "dup-token", "ios");
6156

62-
// Assert
6357
Assert.That(result.Success, Is.True);
6458
Assert.That(await _dbContext.DeviceTokens.CountAsync(), Is.EqualTo(1));
6559

@@ -71,39 +65,19 @@ public async Task RegisterAsync_SameTokenTwice_UpsertsWithoutDuplicate()
7165
[Test]
7266
public async Task UnregisterAsync_ExistingToken_IsRemoved()
7367
{
74-
// Arrange
7568
await _service.RegisterAsync(1, "remove-me", "android");
7669
Assert.That(await _dbContext.DeviceTokens.CountAsync(), Is.EqualTo(1));
7770

78-
// Act
7971
var result = await _service.UnregisterAsync("remove-me");
8072

81-
// Assert
8273
Assert.That(result.Success, Is.True);
83-
Assert.That(await _dbContext.DeviceTokens.CountAsync(), Is.EqualTo(0));
8474
}
8575

8676
[Test]
8777
public async Task UnregisterAsync_NonExistentToken_SucceedsWithoutError()
8878
{
89-
// Act
9079
var result = await _service.UnregisterAsync("does-not-exist");
9180

92-
// Assert
93-
Assert.That(result.Success, Is.True);
94-
}
95-
96-
[Test]
97-
public async Task RegisterAsync_EmptyToken_FailsGracefully()
98-
{
99-
// The DB has a [Required] Token column. With InMemory provider the constraint
100-
// isn't enforced at the DB level, but we still verify the service doesn't crash
101-
// and that an empty-string token is handled.
102-
var result = await _service.RegisterAsync(1, "", "android");
103-
104-
// The service doesn't explicitly validate empty tokens — it succeeds but stores
105-
// an empty string. This test documents the current behaviour; a future guard
106-
// could make this return Success=false.
10781
Assert.That(result.Success, Is.True);
10882
}
10983
}
Lines changed: 5 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
using System.Threading.Tasks;
22
using Microsoft.EntityFrameworkCore;
3-
using Microsoft.Extensions.Configuration;
43
using Microsoft.Extensions.Logging;
4+
using Microting.TimePlanningBase.Infrastructure.Data;
55
using NSubstitute;
66
using NUnit.Framework;
7-
using TimePlanning.Pn.Infrastructure.Models.DeviceToken;
87
using TimePlanning.Pn.Services.PushNotificationService;
98

109
namespace TimePlanning.Pn.Test;
@@ -15,19 +14,15 @@ public class PushNotificationServiceTests
1514
[Test]
1615
public void Constructor_WithoutFirebaseConfig_DoesNotThrow()
1716
{
18-
// Arrange — empty configuration, no Firebase:ServiceAccountPath key
19-
var config = new ConfigurationBuilder().Build();
20-
var options = new DbContextOptionsBuilder<DeviceTokenDbContext>()
17+
var options = new DbContextOptionsBuilder<TimePlanningPnDbContext>()
2118
.UseInMemoryDatabase("PushNotifTest_ctor")
2219
.Options;
23-
var dbContext = new DeviceTokenDbContext(options);
20+
var dbContext = new TimePlanningPnDbContext(options);
2421

25-
// Act & Assert — construction must not throw
2622
Assert.DoesNotThrow(() =>
2723
{
2824
_ = new PushNotificationService(
2925
dbContext,
30-
config,
3126
Substitute.For<ILogger<PushNotificationService>>());
3227
});
3328

@@ -37,47 +32,16 @@ public void Constructor_WithoutFirebaseConfig_DoesNotThrow()
3732
[Test]
3833
public async Task SendToSiteAsync_WhenFirebaseNotConfigured_IsNoOp()
3934
{
40-
// Arrange
41-
var config = new ConfigurationBuilder().Build();
42-
var options = new DbContextOptionsBuilder<DeviceTokenDbContext>()
35+
var options = new DbContextOptionsBuilder<TimePlanningPnDbContext>()
4336
.UseInMemoryDatabase("PushNotifTest_noop")
4437
.Options;
45-
var dbContext = new DeviceTokenDbContext(options);
38+
var dbContext = new TimePlanningPnDbContext(options);
4639
var service = new PushNotificationService(
4740
dbContext,
48-
config,
4941
Substitute.For<ILogger<PushNotificationService>>());
5042

51-
// Act & Assert — should complete without throwing
5243
await service.SendToSiteAsync(1, "Title", "Body");
5344

5445
dbContext.Dispose();
5546
}
56-
57-
[Test]
58-
public void Constructor_WithNonExistentServiceAccountPath_DoesNotThrow()
59-
{
60-
// Arrange — path set but file does not exist
61-
var config = new ConfigurationBuilder()
62-
.AddInMemoryCollection(new[]
63-
{
64-
new System.Collections.Generic.KeyValuePair<string, string?>(
65-
"Firebase:ServiceAccountPath", "/tmp/does-not-exist-firebase.json")
66-
})
67-
.Build();
68-
var options = new DbContextOptionsBuilder<DeviceTokenDbContext>()
69-
.UseInMemoryDatabase("PushNotifTest_badpath")
70-
.Options;
71-
var dbContext = new DeviceTokenDbContext(options);
72-
73-
Assert.DoesNotThrow(() =>
74-
{
75-
_ = new PushNotificationService(
76-
dbContext,
77-
config,
78-
Substitute.For<ILogger<PushNotificationService>>());
79-
});
80-
81-
dbContext.Dispose();
82-
}
8347
}

eFormAPI/Plugins/TimePlanning.Pn/TimePlanning.Pn/EformTimePlanningPlugin.cs

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
4040
using TimePlanning.Pn.Services.PayrollExportService;
4141
using TimePlanning.Pn.Services.DeviceTokenService;
4242
using TimePlanning.Pn.Services.PushNotificationService;
43-
using TimePlanning.Pn.Infrastructure.Models.DeviceToken;
4443
using Constants = Microting.eForm.Infrastructure.Constants.Constants;
4544

4645
namespace TimePlanning.Pn;
@@ -192,14 +191,6 @@ public void ConfigureDbContext(IServiceCollection services, string connectionStr
192191
}));
193192

194193

195-
services.AddDbContext<DeviceTokenDbContext>(o =>
196-
o.UseMySql(connectionString, new MariaDbServerVersion(
197-
ServerVersion.AutoDetect(connectionString)), mySqlOptionsAction: builder =>
198-
{
199-
builder.EnableRetryOnFailure();
200-
builder.MigrationsAssembly(PluginAssembly().FullName);
201-
}));
202-
203194
services.AddDbContext<BaseDbContext>(
204195
o => o.UseMySql(frontendBaseConnectionString, new MariaDbServerVersion(
205196
ServerVersion.AutoDetect(frontendBaseConnectionString)), mySqlOptionsAction: builder =>
@@ -214,23 +205,6 @@ public void ConfigureDbContext(IServiceCollection services, string connectionStr
214205
context.Database.Migrate();
215206
Console.WriteLine("TimePlanningPnDbContext migrated to latest version");
216207

217-
// Ensure DeviceTokens table exists (raw SQL to avoid EnsureCreated conflicts)
218-
Console.WriteLine("Ensuring DeviceTokens table exists");
219-
context.Database.ExecuteSqlRaw(@"
220-
CREATE TABLE IF NOT EXISTS `DeviceTokens` (
221-
`Id` int NOT NULL AUTO_INCREMENT,
222-
`SdkSiteId` int NOT NULL,
223-
`Token` varchar(512) NOT NULL,
224-
`Platform` varchar(16) NOT NULL,
225-
`CreatedAt` datetime(6) NOT NULL,
226-
`UpdatedAt` datetime(6) NOT NULL,
227-
`WorkflowState` varchar(50) NULL DEFAULT 'created',
228-
PRIMARY KEY (`Id`),
229-
UNIQUE INDEX `IX_DeviceTokens_Token` (`Token`),
230-
INDEX `IX_DeviceTokens_SdkSiteId` (`SdkSiteId`)
231-
) CHARACTER SET=utf8mb4;
232-
");
233-
234208
// Seed database
235209
SeedDatabase(connectionString);
236210
}

eFormAPI/Plugins/TimePlanning.Pn/TimePlanning.Pn/Infrastructure/Models/DeviceToken/DeviceToken.cs

Lines changed: 0 additions & 27 deletions
This file was deleted.

eFormAPI/Plugins/TimePlanning.Pn/TimePlanning.Pn/Infrastructure/Models/DeviceToken/DeviceTokenDbContext.cs

Lines changed: 0 additions & 24 deletions
This file was deleted.

eFormAPI/Plugins/TimePlanning.Pn/TimePlanning.Pn/Services/DeviceTokenService/DeviceTokenService.cs

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,19 @@ namespace TimePlanning.Pn.Services.DeviceTokenService;
22

33
using System;
44
using System.Threading.Tasks;
5-
using Infrastructure.Models.DeviceToken;
65
using Microsoft.EntityFrameworkCore;
76
using Microsoft.Extensions.Logging;
87
using Microting.eFormApi.BasePn.Infrastructure.Models.API;
8+
using Microting.TimePlanningBase.Infrastructure.Data;
9+
using Microting.TimePlanningBase.Infrastructure.Data.Entities;
910

1011
public class DeviceTokenService : IDeviceTokenService
1112
{
12-
private readonly DeviceTokenDbContext _dbContext;
13+
private readonly TimePlanningPnDbContext _dbContext;
1314
private readonly ILogger<DeviceTokenService> _logger;
1415

1516
public DeviceTokenService(
16-
DeviceTokenDbContext dbContext,
17+
TimePlanningPnDbContext dbContext,
1718
ILogger<DeviceTokenService> logger)
1819
{
1920
_dbContext = dbContext;
@@ -31,9 +32,7 @@ public async Task<OperationResult> RegisterAsync(int sdkSiteId, string token, st
3132
{
3233
existing.SdkSiteId = sdkSiteId;
3334
existing.Platform = platform;
34-
existing.UpdatedAt = DateTime.UtcNow;
35-
existing.WorkflowState = "created";
36-
_dbContext.DeviceTokens.Update(existing);
35+
await existing.Update(_dbContext);
3736
}
3837
else
3938
{
@@ -42,14 +41,9 @@ public async Task<OperationResult> RegisterAsync(int sdkSiteId, string token, st
4241
SdkSiteId = sdkSiteId,
4342
Token = token,
4443
Platform = platform,
45-
CreatedAt = DateTime.UtcNow,
46-
UpdatedAt = DateTime.UtcNow,
47-
WorkflowState = "created"
4844
};
49-
await _dbContext.DeviceTokens.AddAsync(deviceToken);
45+
await deviceToken.Create(_dbContext);
5046
}
51-
52-
await _dbContext.SaveChangesAsync();
5347
return new OperationResult(true);
5448
}
5549
catch (Exception ex)
@@ -68,8 +62,7 @@ public async Task<OperationResult> UnregisterAsync(string token)
6862

6963
if (existing != null)
7064
{
71-
_dbContext.DeviceTokens.Remove(existing);
72-
await _dbContext.SaveChangesAsync();
65+
await existing.Delete(_dbContext);
7366
}
7467

7568
return new OperationResult(true);

0 commit comments

Comments
 (0)