Skip to content

Commit f947dd1

Browse files
rlabbeptcBoBiene
andauthored
Feat(sdk) modify project properties support (#28)
* feat(api): Add project properties model * feat(api): update property definition class design * feat(api): add project properties to project class * feat(api): add relevant properties to nonserialize and nonupdatable classes * feat(api): add get and set project proptery methods * feat(api): add projectProperties data type * feat(test): add tests for project properties * fix(api): corrected attributes and removed base type for ProjectProperties * fix(test): removed property in test due to base class change * feat(api): Update Project Properties when doing a project comparison * feat(api): Updated CompareAndApply for project properties * chore(api): fix merge --------- Co-authored-by: Bo Biene <23037659+BoBiene@users.noreply.github.com>
1 parent 7c8f093 commit f947dd1

9 files changed

Lines changed: 1809 additions & 9 deletions

File tree

Kepware.Api.Test/ApiClient/AutomaticTagGenerationAsyncTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,8 @@ public async Task AutomaticTagGenerationAsync_ShouldReturnFailure_WhenJobFailsAf
149149
.ReturnsResponse(HttpStatusCode.OK, JsonSerializer.Serialize(jobStatus), "application/json");
150150

151151
// Act
152-
var result = await _kepwareApiClient.ApiServices.AutomaticTagGenerationAsync(UNIT_TEST_CHANNEL, UNIT_TEST_DEVICE, TimeSpan.FromSeconds(2));
153-
var completionResult = await result.AwaitCompletionAsync();
152+
var result = await _kepwareApiClient.ApiServices.AutomaticTagGenerationAsync(UNIT_TEST_CHANNEL, UNIT_TEST_DEVICE, TimeSpan.FromSeconds(1));
153+
var completionResult = await result.AwaitCompletionAsync(TimeSpan.FromMilliseconds(100));
154154

155155
// Assert
156156
completionResult.Value.ShouldBeFalse();
@@ -175,7 +175,7 @@ public async Task AutomaticTagGenerationAsync_ShouldReturnFailure_WhenJobFailsAf
175175

176176
// Act
177177
var result = await _kepwareApiClient.ApiServices.AutomaticTagGenerationAsync(UNIT_TEST_CHANNEL, UNIT_TEST_DEVICE, TimeSpan.FromSeconds(5));
178-
var completionResult = await result.AwaitCompletionAsync();
178+
var completionResult = await result.AwaitCompletionAsync(TimeSpan.FromMilliseconds(100));
179179

180180
// Assert
181181
completionResult.Value.ShouldBeFalse();

Kepware.Api.Test/ApiClient/ProjectPropertiesTests.cs

Lines changed: 389 additions & 0 deletions
Large diffs are not rendered by default.

Kepware.Api.Test/ApiClient/ReinitializeRuntimeAsyncTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,8 @@ public async Task ReinitializeRuntimeAsync_ShouldReturnFailure_WhenJobFailsAfter
147147
.ReturnsResponse(HttpStatusCode.OK, JsonSerializer.Serialize(jobStatus), "application/json");
148148

149149
// Act
150-
var result = await _kepwareApiClient.ApiServices.ReinitializeRuntimeAsync(TimeSpan.FromSeconds(2));
151-
var completionResult = await result.AwaitCompletionAsync();
150+
var result = await _kepwareApiClient.ApiServices.ReinitializeRuntimeAsync(TimeSpan.FromSeconds(1));
151+
var completionResult = await result.AwaitCompletionAsync(TimeSpan.FromMilliseconds(100));
152152

153153
// Assert
154154
completionResult.Value.ShouldBeFalse();
@@ -173,7 +173,7 @@ public async Task ReinitializeRuntimeAsync_ShouldReturnFailure_WhenJobFailsAfter
173173

174174
// Act
175175
var result = await _kepwareApiClient.ApiServices.ReinitializeRuntimeAsync(TimeSpan.FromSeconds(5));
176-
var completionResult = await result.AwaitCompletionAsync();
176+
var completionResult = await result.AwaitCompletionAsync(TimeSpan.FromMilliseconds(100));
177177

178178
// Assert
179179
completionResult.Value.ShouldBeFalse();

Kepware.Api.Test/ApiClient/_TestApiClientBase.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public abstract class TestApiClientBase
2323
protected readonly Mock<HttpMessageHandler> _httpMessageHandlerMock;
2424
protected readonly Mock<ILogger<KepwareApiClient>> _loggerMock;
2525
protected readonly Mock<ILogger<AdminApiHandler>> _loggerMockAdmin;
26+
protected readonly Mock<ILogger<ProjectApiHandler>> _loggerMockProject;
2627
protected readonly Mock<ILogger<GenericApiHandler>> _loggerMockGeneric;
2728
protected readonly Mock<ILoggerFactory> _loggerFactoryMock;
2829
protected readonly KepwareApiClient _kepwareApiClient;
@@ -33,6 +34,7 @@ protected TestApiClientBase()
3334
_loggerMock = new Mock<ILogger<KepwareApiClient>>();
3435
_loggerMockAdmin = new Mock<ILogger<AdminApiHandler>>();
3536
_loggerMockGeneric = new Mock<ILogger<GenericApiHandler>>();
37+
_loggerMockProject = new Mock<ILogger<ProjectApiHandler>>();
3638
_loggerFactoryMock = new Mock<ILoggerFactory>();
3739

3840
_loggerFactoryMock.Setup(factory => factory.CreateLogger(It.IsAny<string>())).Returns((string name) =>
@@ -43,6 +45,8 @@ protected TestApiClientBase()
4345
return _loggerMockAdmin.Object;
4446
else if (name == typeof(GenericApiHandler).FullName)
4547
return _loggerMockGeneric.Object;
48+
else if (name == typeof(ProjectApiHandler).FullName)
49+
return _loggerMockProject.Object;
4650
else
4751
return Mock.Of<ILogger>();
4852
});

Kepware.Api/ClientHandler/ProjectApiHandler.cs

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Diagnostics;
88
using System.Globalization;
99
using System.Linq;
10+
using System.Net.Http;
1011
using System.Text;
1112
using System.Text.Json;
1213
using System.Threading.Tasks;
@@ -67,8 +68,12 @@ public ProjectApiHandler(KepwareApiClient kepwareApiClient, ChannelApiHandler ch
6768
{
6869
if (sourceProject.Hash != projectFromApi.Hash)
6970
{
70-
//TODO update project
71-
m_logger.LogInformation("[not implemented] Project has changed. Updating project...");
71+
m_logger.LogInformation("Project properties has changed. Updating project properties...");
72+
var result = await SetProjectPropertiesAsync(projectFromApi, cancellationToken: cancellationToken).ConfigureAwait(false);
73+
if (!result)
74+
{
75+
m_logger.LogError("Failed to update project properties...");
76+
}
7277
}
7378
int inserts = 0, updates = 0, deletes = 0;
7479

@@ -126,6 +131,76 @@ public ProjectApiHandler(KepwareApiClient kepwareApiClient, ChannelApiHandler ch
126131
}
127132
#endregion
128133

134+
#region ProjectProperties
135+
136+
/// <summary>
137+
/// Gets the project properties from the Kepware server.
138+
/// </summary>
139+
/// <param name="cancellationToken"></param>
140+
/// <returns></returns>
141+
/// <exception cref="InvalidOperationException"></exception>
142+
public async Task<Project?> GetProjectPropertiesAsync(CancellationToken cancellationToken = default)
143+
{
144+
var project = await m_kepwareApiClient.GenericConfig.LoadEntityAsync<Project>(name: null, cancellationToken: cancellationToken).ConfigureAwait(false);
145+
return project;
146+
147+
}
148+
149+
/// <summary>
150+
/// Sets the project properties on the Kepware server.
151+
/// </summary>
152+
/// <param name="project"></param>
153+
/// <param name="cancellationToken"></param>
154+
/// <returns></returns>
155+
/// <exception cref="InvalidOperationException"></exception>
156+
public async Task<bool> SetProjectPropertiesAsync(Project project, CancellationToken cancellationToken = default)
157+
{
158+
try
159+
{
160+
var currentProject = await m_kepwareApiClient.GenericConfig.LoadEntityAsync<Project>(name: null, cancellationToken: cancellationToken).ConfigureAwait(false);
161+
162+
if (currentProject == null)
163+
{
164+
throw new InvalidOperationException("Failed to retrieve current settings");
165+
}
166+
167+
var endpoint = EndpointResolver.ResolveEndpoint<Project>();
168+
var diff = project.GetUpdateDiff(currentProject);
169+
170+
// Need to ensure ProjectId is captured. GetUpdateDiff doesn't return ProjectId on non_NamedEntity types
171+
diff.Add(Properties.ProjectId, KepJsonContext.WrapInJsonElement(currentProject.ProjectId));
172+
173+
m_logger.LogInformation("Updating Project Property Settings on {Endpoint}, values {Diff}", endpoint, diff);
174+
175+
HttpContent httpContent = new StringContent(
176+
JsonSerializer.Serialize(diff, KepJsonContext.Default.DictionaryStringJsonElement),
177+
Encoding.UTF8,
178+
"application/json"
179+
);
180+
181+
var response = await m_kepwareApiClient.HttpClient.PutAsync(endpoint, httpContent, cancellationToken).ConfigureAwait(false);
182+
183+
if (!response.IsSuccessStatusCode)
184+
{
185+
var message = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
186+
m_logger.LogError("Failed to update Project Property Settings from {Endpoint}: {ReasonPhrase}\n{Message}", endpoint, response.ReasonPhrase, message);
187+
}
188+
else
189+
{
190+
return true;
191+
}
192+
}
193+
catch (HttpRequestException httpEx)
194+
{
195+
m_logger.LogWarning(httpEx, "Failed to connect to {BaseAddress}", m_kepwareApiClient.HttpClient.BaseAddress);
196+
m_kepwareApiClient.OnHttpRequestException(httpEx);
197+
}
198+
199+
return false;
200+
}
201+
202+
#endregion
203+
129204
#region LoadProject
130205
/// <summary>
131206
/// Loads the project from the Kepware server.

0 commit comments

Comments
 (0)