Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(NewVersionCheckService): can display different messages based on required, recommened or latest version #1759

Merged
merged 1 commit into from
Jan 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace TeslaSolarCharger.Server.Dtos.Solar4CarBackend;

public class DtoVersionRecommendation(string latestVersion, string recommendedVersion, string minimumVersion)
{
public string LatestVersion { get; set; } = latestVersion;
public string RecommendedVersion { get; set; } = recommendedVersion;
public int? RecommendedVersionRequiredInDays { get; set; }
public string MinimumVersion { get; set; } = minimumVersion;
}
7 changes: 5 additions & 2 deletions TeslaSolarCharger/Server/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ async Task DoStartupStuff(WebApplication webApplication, ILogger<Program> logger
var teslaSolarChargerContext = webApplication.Services.GetRequiredService<ITeslaSolarChargerContext>();
await teslaSolarChargerContext.Database.MigrateAsync().ConfigureAwait(false);

var errorHandlingService = webApplication.Services.GetRequiredService<IErrorHandlingService>();
await errorHandlingService.RemoveInvalidLoggedErrorsAsync().ConfigureAwait(false);


var teslaFleetApiService = webApplication.Services.GetRequiredService<ITeslaFleetApiService>();
await teslaFleetApiService.RefreshFleetApiRequestsAreAllowed();

Expand Down Expand Up @@ -223,11 +227,10 @@ async Task DoStartupStuff(WebApplication webApplication, ILogger<Program> logger
{
await jobManager.StartJobs().ConfigureAwait(false);
}
var errorHandlingService = webApplication.Services.GetRequiredService<IErrorHandlingService>();

var issueKeys = webApplication.Services.GetRequiredService<IIssueKeys>();
await errorHandlingService.HandleErrorResolved(issueKeys.CrashedOnStartup, null)
.ConfigureAwait(false);
await errorHandlingService.RemoveInvalidLoggedErrorsAsync().ConfigureAwait(false);
}
catch (Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

public interface IIssueKeys
{
string VersionNotUpToDate { get; }
string NewSoftwareAvailable { get; }
string NewRecommendedSoftwareAvailable { get; }
string NewRequiredSoftwareAvailable { get; }
string FleetApiTokenUnauthorized { get; }
string NoFleetApiToken { get; }
string FleetApiTokenMissingScopes { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ namespace TeslaSolarCharger.Server.Resources.PossibleIssues;

public class IssueKeys : IIssueKeys
{
public string VersionNotUpToDate => "VersionNotUpToDate";
public string NewSoftwareAvailable => "NewSoftwareAvailable";
public string NewRecommendedSoftwareAvailable => "NewRecommendedSoftwareAvailable";
public string NewRequiredSoftwareAvailable => "NewRequiredSoftwareAvailable";
public string FleetApiTokenUnauthorized => "FleetApiTokenUnauthorized";
public string NoFleetApiToken => "NoFleetApiToken";
public string FleetApiTokenMissingScopes => "FleetApiTokenMissingScopes";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,16 @@ public class PossibleIssues(IIssueKeys issueKeys) : IPossibleIssues
{
private readonly Dictionary<string, DtoIssue> _issues = new()
{
{ issueKeys.VersionNotUpToDate, new DtoIssue
{ issueKeys.NewSoftwareAvailable, new DtoIssue
{
IssueSeverity = IssueSeverity.Information,
IsTelegramEnabled = false,
ShowErrorAfterOccurrences = 1,
HasPlaceHolderIssueKey = false,
HideOccurrenceCount = true,
}
},
{ issueKeys.NewRecommendedSoftwareAvailable, new DtoIssue
{
IssueSeverity = IssueSeverity.Warning,
IsTelegramEnabled = false,
Expand All @@ -18,6 +27,15 @@ public class PossibleIssues(IIssueKeys issueKeys) : IPossibleIssues
HideOccurrenceCount = true,
}
},
{ issueKeys.NewRequiredSoftwareAvailable, new DtoIssue
{
IssueSeverity = IssueSeverity.Error,
IsTelegramEnabled = false,
ShowErrorAfterOccurrences = 1,
HasPlaceHolderIssueKey = false,
HideOccurrenceCount = true,
}
},
{ issueKeys.NoFleetApiToken, new DtoIssue
{
IssueSeverity = IssueSeverity.Error,
Expand Down
17 changes: 9 additions & 8 deletions TeslaSolarCharger/Server/Services/BackendApiService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Reflection;
using TeslaSolarCharger.Model.Contracts;
using TeslaSolarCharger.Model.Entities.TeslaSolarCharger;
using TeslaSolarCharger.Server.Dtos.Solar4CarBackend;
using TeslaSolarCharger.Server.Dtos.Solar4CarBackend.User;
using TeslaSolarCharger.Server.Dtos.TscBackend;
using TeslaSolarCharger.Server.Resources.PossibleIssues.Contracts;
Expand Down Expand Up @@ -163,8 +164,7 @@ internal string GenerateAuthUrl(DtoTeslaOAuthRequestInformation oAuthInformation
return url;
}

public async Task PostInstallationInformation(string reason)

public async Task<DtoVersionRecommendation> PostInstallationInformation(string reason)
{
try
{
Expand All @@ -177,33 +177,34 @@ public async Task PostInstallationInformation(string reason)
if (tokenState == TokenState.UpToDate)
{
var token = await teslaSolarChargerContext.BackendTokens.SingleAsync();
var result = await SendRequestToBackend<object>(HttpMethod.Post, token.AccessToken,
var result = await SendRequestToBackend<DtoVersionRecommendation>(HttpMethod.Post, token.AccessToken,
$"Client/NotifyInstallation?version={Uri.EscapeDataString(currentVersion ?? string.Empty)}&infoReason={Uri.EscapeDataString(reason)}",
null);
if (!result.HasError)
{
logger.LogInformation("Sent installation information to Backend");
return;
return result.Data ?? throw new InvalidOperationException("Could not deserialize Version recommendation");
}

logger.LogWarning("Error while sending installation information to backend. {errorMessage}", result.ErrorMessage);
throw new InvalidOperationException(result.ErrorMessage);
}
var noTokenResult = await SendRequestToBackend<object>(HttpMethod.Post, null,
var noTokenResult = await SendRequestToBackend<DtoVersionRecommendation>(HttpMethod.Post, null,
$"Client/NotifyInstallationAnonymous?version={Uri.EscapeDataString(currentVersion ?? string.Empty)}&infoReason={Uri.EscapeDataString(reason)}&installationId={Uri.EscapeDataString(installationId.ToString())}",
null);
if (!noTokenResult.HasError)
{
logger.LogInformation("Sent installation information to Backend");
return;
return noTokenResult.Data ?? throw new InvalidOperationException("Could not deserialize Version recommendation");
}

logger.LogWarning("Error while sending installation information to backend. {errorMessage}", noTokenResult.ErrorMessage);
throw new InvalidOperationException(noTokenResult.ErrorMessage);
}
catch (Exception e)
{
logger.LogError(e, "Could not post installation information");
throw;
}

}

public Task<string?> GetCurrentVersion()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
using TeslaSolarCharger.Model.Entities.TeslaSolarCharger;
using TeslaSolarCharger.Server.Dtos.Solar4CarBackend;
using TeslaSolarCharger.Shared.Dtos;

namespace TeslaSolarCharger.Server.Services.Contracts;

public interface IBackendApiService
{
Task<DtoValue<string>> StartTeslaOAuth(string locale, string baseUrl);
Task PostInstallationInformation(string reason);
Task<DtoVersionRecommendation> PostInstallationInformation(string reason);
Task<string?> GetCurrentVersion();
Task GetNewBackendNotifications();
Task GetToken(DtoBackendLogin login);
Expand Down
3 changes: 0 additions & 3 deletions TeslaSolarCharger/Server/Services/ErrorHandlingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,6 @@ await context.ModbusResultConfigurations.Where(r => r.UsedFor <= ValueUsage.Home
await AddOrRemoveErrors(activeErrors, issueKeys.SolarValuesNotAvailable, "Solar values are not available",
$"Solar values are {pvValueUpdateAge} old. It looks like there is something wrong when trying to get the solar values.", solarValuesTooOld).ConfigureAwait(false);

await AddOrRemoveErrors(activeErrors, issueKeys.VersionNotUpToDate, "New software version available",
"Update TSC to the latest version.", settings.IsNewVersionAvailable).ConfigureAwait(false);

//ToDO: fix next line, currently not working due to cyclic reference
//await AddOrRemoveErrors(activeErrors, issueKeys.BaseAppNotLicensed, "Base App not licensed",
// "Can not send commands to car as app is not licensed", !await backendApiService.IsBaseAppLicensed(true));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Globalization;
using System.Net.WebSockets;
using System.Text;
using TeslaSolarCharger.Model.Entities.TeslaMate;
using TeslaSolarCharger.Model.Entities.TeslaSolarCharger;
using TeslaSolarCharger.Model.EntityFramework;
using TeslaSolarCharger.Server.Dtos;
Expand Down
86 changes: 52 additions & 34 deletions TeslaSolarCharger/Server/Services/NewVersionCheckService.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using TeslaSolarCharger.Server.Contracts;
using TeslaSolarCharger.Server.Dtos.TscBackend;
using TeslaSolarCharger.Server.Resources.PossibleIssues.Contracts;
using TeslaSolarCharger.Server.Services.Contracts;
using TeslaSolarCharger.Shared.Contracts;
using TeslaSolarCharger.Shared.Dtos.Contracts;
Expand All @@ -12,64 +13,81 @@ public class NewVersionCheckService : INewVersionCheckService
private readonly ICoreService _coreService;
private readonly ISettings _settings;
private readonly IBackendApiService _backendApiService;
private readonly IErrorHandlingService _errorHandlingService;
private readonly IIssueKeys _issueKeys;

public NewVersionCheckService(ILogger<NewVersionCheckService> logger, ICoreService coreService, ISettings settings,
IBackendApiService backendApiService)
IBackendApiService backendApiService, IErrorHandlingService errorHandlingService, IIssueKeys issueKeys)
{
_logger = logger;
_coreService = coreService;
_settings = settings;
_backendApiService = backendApiService;
_errorHandlingService = errorHandlingService;
_issueKeys = issueKeys;
}

public async Task CheckForNewVersion()
{
_logger.LogTrace("{method}()", nameof(CheckForNewVersion));
var currentVersion = await _coreService.GetCurrentVersion().ConfigureAwait(false);
await _backendApiService.PostInstallationInformation("CheckForNewVersion").ConfigureAwait(false);
if (string.IsNullOrEmpty(currentVersion))
var versionRecommendation = await _backendApiService.PostInstallationInformation("CheckForNewVersion").ConfigureAwait(false);
var couldParseLocalVersion = Version.TryParse(currentVersion, out var localVersion);
if (!couldParseLocalVersion)
{
_settings.IsNewVersionAvailable = false;
return;
}


try
{
if (currentVersion.Contains("-"))
{
currentVersion = currentVersion.Split("-").First();
}
var localVersion = Version.Parse(currentVersion);
_logger.LogDebug("Local version is {localVersion}", localVersion);
var finalUrl = await GetRedirectedUrlAsync("https://github.com/pkuehnel/TeslaSolarCharger/releases/latest").ConfigureAwait(false);
if (string.IsNullOrEmpty(finalUrl))
if (string.IsNullOrEmpty(currentVersion))
{
_settings.IsNewVersionAvailable = false;
_logger.LogError("Could not get local version");
return;
}
var tag = finalUrl.Split("/").Last();
var githubVersionString = tag.Substring(1);
if (string.IsNullOrEmpty(githubVersionString))
var splittedVersionString= currentVersion.Split("-")[0];
couldParseLocalVersion = Version.TryParse(splittedVersionString, out localVersion);
if(!couldParseLocalVersion || localVersion == default)
{
_settings.IsNewVersionAvailable = false;
return;
}
var githubVersion = Version.Parse(githubVersionString);
_logger.LogDebug("Local version is {githubVersion}", githubVersionString);
if (githubVersion > localVersion)
{
_settings.IsNewVersionAvailable = true;
_logger.LogError("Could not parse local version {currentVersion}", currentVersion);
return;
}
var buildToUse = localVersion.Build > 0 ? localVersion.Build - 1 : 0;
localVersion = new(localVersion.Major, localVersion.Minor, buildToUse);
}
var minimumVersion = Version.Parse(versionRecommendation.MinimumVersion);
if (localVersion < minimumVersion)
{
await _errorHandlingService.HandleError(nameof(NewVersionCheckService), nameof(CheckForNewVersion), "New version required",
"You need to update to the latest version as TSC won't work anymore", _issueKeys.NewRequiredSoftwareAvailable, null, null).ConfigureAwait(false);
await _errorHandlingService.HandleErrorResolved(_issueKeys.NewRecommendedSoftwareAvailable, null).ConfigureAwait(false);
await _errorHandlingService.HandleErrorResolved(_issueKeys.NewSoftwareAvailable, null).ConfigureAwait(false);
return;
}
else
{
await _errorHandlingService.HandleErrorResolved(_issueKeys.NewRequiredSoftwareAvailable, null).ConfigureAwait(false);
}

var recommendedVersion = Version.Parse(versionRecommendation.RecommendedVersion);
if (localVersion < recommendedVersion)
{
var headLineText = versionRecommendation.RecommendedVersionRequiredInDays == default ? "New version recommended" : $"New Version required in {versionRecommendation.RecommendedVersionRequiredInDays} days";
var messageText = versionRecommendation.RecommendedVersionRequiredInDays == default ? "It is recommended to update to the latest version" : $"After {versionRecommendation.RecommendedVersionRequiredInDays} days your current installed version won't work anymore. Please update as soon as possible.";
await _errorHandlingService.HandleError(nameof(NewVersionCheckService), nameof(CheckForNewVersion), headLineText,
messageText, _issueKeys.NewRecommendedSoftwareAvailable, null, null).ConfigureAwait(false);
await _errorHandlingService.HandleErrorResolved(_issueKeys.NewSoftwareAvailable, null).ConfigureAwait(false);
return;
}
else
{
await _errorHandlingService.HandleErrorResolved(_issueKeys.NewRecommendedSoftwareAvailable, null).ConfigureAwait(false);
}
catch (Exception ex)
var latestVersion = Version.Parse(versionRecommendation.LatestVersion);
if (localVersion < latestVersion)
{
_logger.LogError(ex, "Couldn't check for new version");
_settings.IsNewVersionAvailable = false;
var headLineText = "New version available";
var messageText = "Update to the latest version to get the latest new features";
await _errorHandlingService.HandleError(nameof(NewVersionCheckService), nameof(CheckForNewVersion), headLineText,
messageText, _issueKeys.NewSoftwareAvailable, null, null).ConfigureAwait(false);
return;
}
_settings.IsNewVersionAvailable = false;
await _errorHandlingService.HandleErrorResolved(_issueKeys.NewSoftwareAvailable, null).ConfigureAwait(false);
}

private async Task<string?> GetRedirectedUrlAsync(string uri)
Expand Down
1 change: 0 additions & 1 deletion TeslaSolarCharger/Shared/Dtos/Contracts/ISettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ public interface ISettings
int? HomeBatterySoc { get; set; }
int? HomeBatteryPower { get; set; }
bool ControlledACarAtLastCycle { get; set; }
bool IsNewVersionAvailable { get; set; }
DateTimeOffset LastPvValueUpdate { get; set; }
int? AverageHomeGridVoltage { get; set; }
bool CrashedOnStartup { get; set; }
Expand Down
1 change: 0 additions & 1 deletion TeslaSolarCharger/Shared/Dtos/Settings/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ namespace TeslaSolarCharger.Shared.Dtos.Settings;

public class Settings : ISettings
{
public bool IsNewVersionAvailable { get; set; }
public int? InverterPower { get; set; }
public int? Overage { get; set; }
public List<DtoCar> CarsToManage => Cars.Where(c => c.ShouldBeManaged == true).OrderBy(c => c.ChargingPriority).ToList();
Expand Down
Loading