Skip to content

Commit 4a65372

Browse files
committed
fix(sdk): Refactored logic and consolidated connected status control to just TestConnectionAsync
1 parent 8cc76b8 commit 4a65372

1 file changed

Lines changed: 57 additions & 26 deletions

File tree

Kepware.Api/KepwareApiClient.cs

Lines changed: 57 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -117,57 +117,90 @@ internal KepwareApiClient(string name, KepwareApiClientOptions options, ILoggerF
117117

118118
#region connection test & product info
119119
/// <summary>
120-
/// Tests the connection to the Kepware server and checks if the server runtime is healthy. Uses the
121-
/// /config/v1/status endpoint for health verification.
120+
/// Tests the connection to the Kepware server and checks if the server runtime is healthy. Also
121+
/// validates authentication credentials.
122+
/// Uses the /config/v1/status endpoint for health verification.
123+
/// Uses the /config/v1/doc endpoint to verify credentials.
122124
/// </summary>
123125
/// <param name="cancellationToken">The cancellation token.</param>
124126
/// <returns>A task that represents the asynchronous operation. The task result contains a boolean indicating whether the connection was successful.</returns>
125127

126128
public async Task<bool> TestConnectionAsync(CancellationToken cancellationToken = default)
127129
{
128-
bool blnIsConnected = false;
129130
try
130131
{
131-
if (m_isConnected == null) // first time after connection change
132+
if (m_isConnected != true) // already connected
132133
{
133134
m_logger.LogInformation("Connecting to {ClientName}-client at {BaseAddress}...", ClientName, m_httpClient.BaseAddress);
134135
}
135136
var response = await m_httpClient.GetAsync(ENDPOINT_STATUS, cancellationToken).ConfigureAwait(false);
136137

137-
if (response.IsSuccessStatusCode)
138+
// check if the response is successful and contains a healthy status
139+
// if the response is not successful, we assume the connection is not healthy
140+
if (!response.IsSuccessStatusCode)
138141
{
139-
var status = await JsonSerializer.DeserializeAsync(
142+
m_logger.LogWarning("Failed to connect to {ClientName}-client at {BaseAddress}, Reason: {ReasonPhrase}", ClientName, m_httpClient.BaseAddress, response.ReasonPhrase);
143+
m_isConnected = null; // set connection state to null if we cannot connect
144+
return false; // connection failed
145+
}
146+
147+
// Deserialize the response content to check the status
148+
var status = await JsonSerializer.DeserializeAsync(
140149
await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false),
141150
KepJsonContext.Default.ListApiStatus, cancellationToken)
142151
.ConfigureAwait(false);
143-
if (status?.FirstOrDefault()?.Healthy == true)
144-
{
145-
blnIsConnected = true;
146-
}
152+
153+
// Check if the status is healthy
154+
if (status?.FirstOrDefault()?.Healthy == false)
155+
{
156+
m_logger.LogWarning("Failed to connect to {ClientName}-client at {BaseAddress}, Reason: {String}", ClientName, m_httpClient.BaseAddress, "Server Status Check Failed");
157+
m_isConnected = null; // set connection state to null if we cannot connect
158+
return false; // connection failed
147159
}
148160

149-
if (m_isConnected == null || (m_isConnected != null && m_isConnected != blnIsConnected)) // first time after connection change or when connection is lost
161+
// If the connection is already healthy, we can return true immediately
162+
if (m_isConnected == true )
150163
{
151-
if (!blnIsConnected)
152-
{
153-
m_logger.LogWarning("Failed to connect to {ClientName}-client at {BaseAddress}, Reason: {ReasonPhrase}", ClientName, m_httpClient.BaseAddress, response.ReasonPhrase);
154-
}
155-
else
156-
{
157-
var prodInfo = await GetProductInfoAsync(cancellationToken).ConfigureAwait(false);
158-
m_logger.LogInformation("Successfully connected to {ClientName}-client: {ProductName} {ProductVersion} on {BaseAddress}", ClientName, prodInfo?.ProductName, prodInfo?.ProductVersion, m_httpClient.BaseAddress);
159-
160-
m_hasValidCredentials = await TestCredentialsAsync(cancellationToken).ConfigureAwait(false);
161-
}
164+
return true; // connection is healthy
162165
}
166+
167+
// Inital connection attempt or a reconnection due to failure,
168+
// we need to check the product info and credentials
169+
var prodInfo = await GetProductInfoAsync(cancellationToken).ConfigureAwait(false);
170+
171+
// If we cannot get the product info, we assume the connection is not healthy
172+
if (prodInfo == null)
173+
{
174+
m_isConnected = null; // set connection state to null if we cannot get product info
175+
return false;
176+
}
177+
178+
// If we have a valid product info, we can check the credentials
179+
m_hasValidCredentials = await TestCredentialsAsync(cancellationToken).ConfigureAwait(false);
180+
181+
// If we do not have valid credentials, we assume the connection is not healthy
182+
if (m_hasValidCredentials != true)
183+
{
184+
m_isConnected = null; // set connection state to null if we cannot connect or credentials are invalid
185+
m_logger.LogWarning("Connection to {ClientName}-client at {BaseAddress} failed because credentials are invalid", ClientName, m_httpClient.BaseAddress);
186+
return false;
187+
}
188+
189+
m_logger.LogInformation("Successfully connected to {ClientName}-client: {ProductName} {ProductVersion} on {BaseAddress}", ClientName, prodInfo?.ProductName, prodInfo?.ProductVersion, m_httpClient.BaseAddress);
190+
191+
m_isConnected = true; // set connection state to true if we have a valid product info and credentials
192+
return m_isConnected.Value; // return true if we have a valid connection
193+
163194
}
164195
catch (HttpRequestException httpEx)
165196
{
166197
if (m_isConnected == null || m_isConnected == true) // first time after connection change or when connection is lost
167198
m_logger.LogWarning(httpEx, "Failed to connect to {ClientName}-client at {BaseAddress}", ClientName, m_httpClient.BaseAddress);
199+
m_isConnected = null;
168200
}
169-
m_isConnected = blnIsConnected;
170-
return blnIsConnected && m_hasValidCredentials == true;
201+
202+
// If we reach this point, we assume the connection is not healthy
203+
return false;
171204
}
172205

173206
/// <summary>
@@ -186,7 +219,6 @@ await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false
186219
var content = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
187220
var prodInfo = JsonSerializer.Deserialize(content, KepJsonContext.Default.ProductInfo);
188221

189-
m_isConnected = true;
190222
return prodInfo;
191223
}
192224
else
@@ -197,7 +229,6 @@ await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false
197229
catch (HttpRequestException httpEx)
198230
{
199231
m_logger.LogWarning(httpEx, "Failed to connect to {ClientName}-client at {BaseAddress}: {Message}", ClientName, m_httpClient.BaseAddress, httpEx.Message);
200-
m_isConnected = null;
201232
}
202233
catch (JsonException jsonEx)
203234
{

0 commit comments

Comments
 (0)