Skip to content
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
3 changes: 0 additions & 3 deletions ImmichFrame.Core/Interfaces/IServerSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ public interface IAccountSettings
public List<Guid> ExcludedAlbums { get; }
public List<Guid> People { get; }
public int? Rating { get; }
public string ImmichFrameAlbumName { get; }
}

public interface IGeneralSettings
Expand All @@ -32,7 +31,6 @@ public interface IGeneralSettings
public string? UnitSystem { get; }
public string? Webhook { get; }
public string? AuthenticationSecret { get; }
public string Margin { get; }
public int Interval { get; }
public double TransitionDuration { get; }
public bool DownloadImages { get; }
Expand All @@ -52,7 +50,6 @@ public interface IGeneralSettings
public string Style { get; }
public string? BaseFontSize { get; }
public bool ShowWeatherDescription { get; }
public bool UnattendedMode { get; }
public bool ImageZoom { get; }
public bool ImagePan { get; }
public bool ImageFill { get; }
Expand Down
16 changes: 12 additions & 4 deletions ImmichFrame.WebApi.Tests/Helpers/Config/ConfigLoaderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
{
var jsonConfig = _configLoader.LoadConfigJson<ServerSettingsV1>(Path.Combine(
TestContext.CurrentContext.TestDirectory, "Resources/TestV1.json"));

var config = _configLoader.LoadConfigFromDictionary<ServerSettingsV1>(ToDictionary(jsonConfig));
VerifyConfig(new ServerSettingsV1Adapter(config), false);
}
Expand All @@ -49,6 +49,14 @@
VerifyConfig(config, true);
}

[Test]
public void TestLoadConfigV2Yaml()
{
var config = _configLoader.LoadConfigYaml<ServerSettings>(Path.Combine(
TestContext.CurrentContext.TestDirectory, "Resources/TestV2.yml"));
VerifyConfig(config, true);
}

private void VerifyConfig(IServerSettings serverSettings, Boolean usePrefix)
{
VerifyProperties(serverSettings.GeneralSettings);
Expand All @@ -72,7 +80,7 @@
if (type.IsGenericType && typeof(IEnumerable).IsAssignableFrom(type))
{
type = type.GetGenericArguments()[0];
value = (value as IEnumerable).Cast<object>().FirstOrDefault();

Check warning on line 83 in ImmichFrame.WebApi.Tests/Helpers/Config/ConfigLoaderTest.cs

View workflow job for this annotation

GitHub Actions / test

Possible null reference argument for parameter 'source' in 'IEnumerable<object> Enumerable.Cast<object>(IEnumerable source)'.
}

//if it's nullable, unwrap
Expand Down Expand Up @@ -106,7 +114,7 @@
}
}
}

public static IDictionary ToDictionary(object obj, bool ignoreNullValues = false)
{
if (obj == null)
Expand All @@ -125,7 +133,7 @@
// Ensure the property has a public getter
if (prop.CanRead && prop.GetMethod?.IsPublic == true)
{
object value = prop.GetValue(obj);

Check warning on line 136 in ImmichFrame.WebApi.Tests/Helpers/Config/ConfigLoaderTest.cs

View workflow job for this annotation

GitHub Actions / test

Converting null literal or possible null value to non-nullable type.

if (ignoreNullValues && value == null)
{
Expand All @@ -134,18 +142,18 @@

if (!(value is string) && value is IEnumerable)
{
value = string.Join(",", (value as IEnumerable).Cast<object>().Select(x => x.ToString()));

Check warning on line 145 in ImmichFrame.WebApi.Tests/Helpers/Config/ConfigLoaderTest.cs

View workflow job for this annotation

GitHub Actions / test

Possible null reference argument for parameter 'source' in 'IEnumerable<object> Enumerable.Cast<object>(IEnumerable source)'.
}
else
{
value = value.ToString();

Check warning on line 149 in ImmichFrame.WebApi.Tests/Helpers/Config/ConfigLoaderTest.cs

View workflow job for this annotation

GitHub Actions / test

Converting null literal or possible null value to non-nullable type.

Check warning on line 149 in ImmichFrame.WebApi.Tests/Helpers/Config/ConfigLoaderTest.cs

View workflow job for this annotation

GitHub Actions / test

Dereference of a possibly null reference.
}

dictionary.Add(prop.Name, value);
}
}

return dictionary;
}

}
54 changes: 29 additions & 25 deletions ImmichFrame.WebApi.Tests/ImmichFrame.WebApi.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,31 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="NUnit" Version="4.3.2" />
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="NUnit" Version="4.3.2" />
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\ImmichFrame.WebApi\ImmichFrame.WebApi.csproj" />
</ItemGroup>

<ItemGroup>
<None Remove="Resources\TestV1.json" />
<Content Include="Resources\TestV1.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Remove="Resources\TestV2.json" />
<Content Include="Resources\TestV2.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ImmichFrame.WebApi\ImmichFrame.WebApi.csproj" />
</ItemGroup>

<ItemGroup>
<None Remove="Resources\TestV1.json" />
<Content Include="Resources\TestV1.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Remove="Resources\TestV2.json" />
<Content Include="Resources\TestV2.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Remove="Resources\TestV2.yml" />
<Content Include="Resources\TestV2.yml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

</Project>
5 changes: 1 addition & 4 deletions ImmichFrame.WebApi.Tests/Resources/TestV1.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
"ShowPeopleDesc": true,
"ShowImageLocation": true,
"ImageLocationFormat": "ImageLocationFormat_TEST",
"ImmichFrameAlbumName": "ImmichFrameAlbumName_TEST",
"PrimaryColor": "PrimaryColor_TEST",
"SecondaryColor": "SecondaryColor_TEST",
"Style": "Style_TEST",
Expand All @@ -52,7 +51,5 @@
"Webhook": "Webhook_TEST",
"Account2.ImmichServerUrl": "Account2.ImmichServerUrl_TEST",
"Account2.ApiKey": "Account2.ApiKey_TEST",
"Account2.ImagesFromDate": "Account2.ImagesFromDate_TEST",
"Margin": "Margin_TEST",
"UnattendedMode": true
"Account2.ImagesFromDate": "Account2.ImagesFromDate_TEST"
}
8 changes: 2 additions & 6 deletions ImmichFrame.WebApi.Tests/Resources/TestV2.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
"WeatherLatLong": "WeatherLatLong_TEST",
"Language": "Language_TEST",
"Webhook": "Webhook_TEST",
"Margin": "Margin_TEST",
"Interval": 7,
"TransitionDuration": 7.7,
"ShowClock": true,
Expand All @@ -30,7 +29,6 @@
"Style": "Style_TEST",
"BaseFontSize": "BaseFontSize_TEST",
"ShowWeatherDescription": true,
"UnattendedMode": true,
"ImageZoom": true,
"ImagePan": true,
"ImageFill": true,
Expand All @@ -55,8 +53,7 @@
],
"People": [
"00000000-0000-0000-0000-000000000001"
],
"ImmichFrameAlbumName": "Account1.ImmichFrameAlbumName_TEST"
]
},
{
"ImmichServerUrl": "Account2.ImmichServerUrl_TEST",
Expand All @@ -76,8 +73,7 @@
],
"People": [
"00000000-0000-0000-0000-000000000001"
],
"ImmichFrameAlbumName": "Account2.ImmichFrameAlbumName_TEST"
]
}
]
}
65 changes: 65 additions & 0 deletions ImmichFrame.WebApi.Tests/Resources/TestV2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
General:
AuthenticationSecret: AuthenticationSecret_TEST
DownloadImages: true
RenewImagesDuration: 7
Webcalendars:
- Webcalendars_TEST
RefreshAlbumPeopleInterval: 7
PhotoDateFormat: PhotoDateFormat_TEST
ImageLocationFormat: ImageLocationFormat_TEST
WeatherApiKey: WeatherApiKey_TEST
UnitSystem: UnitSystem_TEST
WeatherLatLong: WeatherLatLong_TEST
Language: Language_TEST
Webhook: Webhook_TEST
Interval: 7
TransitionDuration: 7.7
ShowClock: true
ClockFormat: ClockFormat_TEST
ShowProgressBar: true
ShowPhotoDate: true
ShowImageDesc: true
ShowPeopleDesc: true
ShowAlbumName: true
ShowImageLocation: true
PrimaryColor: PrimaryColor_TEST
SecondaryColor: SecondaryColor_TEST
Style: Style_TEST
BaseFontSize: BaseFontSize_TEST
ShowWeatherDescription: true
ImageZoom: true
ImagePan: true
ImageFill: true
Layout: Layout_TEST
Accounts:
- ImmichServerUrl: Account1.ImmichServerUrl_TEST
ApiKey: Account1.ApiKey_TEST
ImagesFromDate: '2020-01-02'
ShowMemories: true
ShowFavorites: true
ShowArchived: true
ImagesFromDays: 7
ImagesUntilDate: '2020-01-02'
Rating: 7
Albums:
- 00000000-0000-0000-0000-000000000001
ExcludedAlbums:
- 00000000-0000-0000-0000-000000000001
People:
- 00000000-0000-0000-0000-000000000001
- ImmichServerUrl: Account2.ImmichServerUrl_TEST
ApiKey: Account2.ApiKey_TEST
ImagesFromDate: '2020-01-02'
ShowMemories: true
ShowFavorites: true
ShowArchived: true
ImagesFromDays: 7
ImagesUntilDate: '2020-01-02'
Rating: 7
Albums:
- 00000000-0000-0000-0000-000000000001
ExcludedAlbums:
- 00000000-0000-0000-0000-000000000001
People:
- 00000000-0000-0000-0000-000000000001
84 changes: 66 additions & 18 deletions ImmichFrame.WebApi/Helpers/Config/ConfigLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,58 @@
using ImmichFrame.Core.Exceptions;
using ImmichFrame.Core.Interfaces;
using ImmichFrame.WebApi.Models;
using YamlDotNet.Serialization;

namespace ImmichFrame.WebApi.Helpers.Config;

public class ConfigLoader(ILogger<ConfigLoader> _logger)
{
public IServerSettings LoadConfig(string filename)
public IServerSettings LoadConfig(string configPath)
{
try
{
return LoadConfigJson<ServerSettings>(filename);
}
catch (Exception e)
var jsonConfigPath = Path.Combine(configPath, "Settings.json");
if (File.Exists(jsonConfigPath))
{
_logger.LogWarning("Failed to load config as current version, falling back to old version (${errorMessage})", e.Message);
}
try
{
return LoadConfigJson<ServerSettings>(jsonConfigPath);
}
catch (Exception e)
{
_logger.LogWarning("Failed to load config as current version JSON. ({errorMessage})", e.Message);
}

try
{
var v1 = LoadConfigJson<ServerSettingsV1>(filename);
return new ServerSettingsV1Adapter(v1);
try
{
var v1 = LoadConfigJson<ServerSettingsV1>(jsonConfigPath);
return new ServerSettingsV1Adapter(v1);
}
catch (Exception e)
{
_logger.LogWarning("Failed to load config as old JSON. ({errorMessage})", e.Message);
}
}
catch (Exception e)

var ymlConfigPath = Path.Combine(configPath, "Settings.yml");
if (File.Exists(ymlConfigPath))
{
_logger.LogWarning("Failed to load config as old JSON, falling back to env vars (${errorMessage})", e.Message);
try
{
return LoadConfigYaml<ServerSettings>(ymlConfigPath);
}
catch (Exception e)
{
_logger.LogWarning("Failed to load config as current version YAML. ({errorMessage})", e.Message);
}

try
{
var v1 = LoadConfigYaml<ServerSettingsV1>(ymlConfigPath);
return new ServerSettingsV1Adapter(v1);
}
catch (Exception e)
{
_logger.LogWarning("Failed to load config as old YAML. ({errorMessage})", e.Message);
}
}

try
Expand All @@ -36,9 +64,9 @@ public IServerSettings LoadConfig(string filename)
}
catch (Exception e)
{
_logger.LogWarning("Failed to load config as env vars (${errorMessage})", e.Message);
_logger.LogWarning("Failed to load config as env vars ({errorMessage})", e.Message);
}

throw new ImmichFrameException("Failed to load configuration");
}

Expand All @@ -64,7 +92,7 @@ public IServerSettings LoadConfig(string filename)
{
throw new ImmichFrameException("No environment variables found");
}

return config;
}

Expand All @@ -78,7 +106,27 @@ public IServerSettings LoadConfig(string filename)
var doc = JsonDocument.Parse(json);
return doc.Deserialize<T>() ?? throw new FileLoadException("Failed to load config file", configPath);
}


throw new FileNotFoundException(configPath);
}
catch (Exception ex)
{
throw new SettingsNotValidException($"Problem with parsing the settings: {ex.Message}", ex);
}
}
internal T LoadConfigYaml<T>(string configPath) where T : IConfigSettable, new()
{
try
{
if (File.Exists(configPath))
{
var yml = File.ReadAllText(configPath);
var deserializer = new DeserializerBuilder()
.IgnoreUnmatchedProperties()
.Build();
return deserializer.Deserialize<T>(yml) ?? throw new FileLoadException("Failed to load config file", configPath);
}

throw new FileNotFoundException(configPath);
}
catch (Exception ex)
Expand Down
Loading
Loading