Compare commits

..

3 Commits

17 changed files with 283 additions and 67 deletions

View File

@ -4,5 +4,12 @@
"Port": 5432, "Port": 5432,
"Username": "postgres", "Username": "postgres",
"Password": "q" "Password": "q"
} },
"KeycloakTestUser": {
"username": "myuser",
"password": 12345,
"clientId": "webapi",
"grantType": "password"
},
"KeycloakGetTokenUrl": "http://192.168.0.10:8321/realms/Persistence/protocol/openid-connect/token"
} }

View File

@ -10,9 +10,9 @@
}, },
"AllowedHosts": "*", "AllowedHosts": "*",
"Authentication": { "Authentication": {
"MetadataAddress": "http://localhost:8080/realms/TestRealm/.well-known/openid-configuration", "MetadataAddress": "http://192.168.0.10:8321/realms/Persistence/.well-known/openid-configuration",
"Audience": "account", "Audience": "account",
"ValidIssuer": "http://localhost:8080/realms/TestRealm", "ValidIssuer": "http://192.168.0.10:8321/realms/Persistence",
"AuthorizationUrl": "http://localhost:8080/realms/TestRealm/protocol/openid-connect/auth" "AuthorizationUrl": "http://192.168.0.10:8321/realms/Persistence/protocol/openid-connect/auth"
} }
} }

View File

@ -0,0 +1,24 @@
using Persistence.Models;
using Refit;
namespace Persistence.Client.Clients;
/// <summary>
/// Интерфейс для тестирования API, предназначенного для работы с уставками
/// </summary>
public interface ISetpointClient
{
private const string BaseRoute = "/api/setpoint";
[Get($"{BaseRoute}/current")]
Task<IApiResponse<IEnumerable<SetpointValueDto>>> GetCurrent([Query(CollectionFormat.Multi)] IEnumerable<Guid> setpointKeys);
[Get($"{BaseRoute}/history")]
Task<IApiResponse<IEnumerable<SetpointValueDto>>> GetHistory([Query(CollectionFormat.Multi)] IEnumerable<Guid> setpointKeys, [Query] DateTimeOffset historyMoment);
[Get($"{BaseRoute}/log")]
Task<IApiResponse<Dictionary<Guid, IEnumerable<SetpointLogDto>>>> GetLog([Query(CollectionFormat.Multi)] IEnumerable<Guid> setpointKeys);
[Post($"{BaseRoute}/")]
Task<IApiResponse> Save(Guid setpointKey, object newValue);
}

View File

@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Refit; using Refit;
namespace Persistence.IntegrationTests.Clients; namespace Persistence.Client.Clients;
public interface ITimeSeriesClient<TDto> public interface ITimeSeriesClient<TDto>
where TDto : class, new() where TDto : class, new()
{ {

View File

@ -0,0 +1,43 @@
namespace Persistence.Client.Helpers;
public static class ApiTokenHelper
{
public static string GetAdminUserToken()
{
//var user = new User()
//{
// Id = 1,
// IdCompany = 1,
// Login = "test_user"
//};
//var roles = new[] { "root" };
return string.Empty;
}
//private static string CreateToken(User user, IEnumerable<string> roles)
//{
// var claims = new List<Claim>
// {
// new("id", user.Id.ToString()),
// new(ClaimsIdentity.DefaultNameClaimType, user.Login),
// new("idCompany", user.IdCompany.ToString()),
// };
// claims.AddRange(roles.Select(role => new Claim(ClaimTypes.Role, role)));
// const string secret = "супер секретный ключ для шифрования";
// var key = Encoding.ASCII.GetBytes(secret);
// var tokenDescriptor = new SecurityTokenDescriptor
// {
// Issuer = "a",
// Audience = "a",
// Subject = new ClaimsIdentity(claims),
// Expires = DateTime.UtcNow.AddHours(1),
// SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
// };
// var tokenHandler = new JwtSecurityTokenHandler();
// var token = tokenHandler.CreateToken(tokenDescriptor);
// return tokenHandler.WriteToken(token);
//}
}

View File

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Refit" Version="8.0.0" />
<PackageReference Include="Refit.HttpClientFactory" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Persistence\Persistence.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using Refit;
using Persistence.Client.Helpers;
namespace Persistence.Client
{
public static class PersistenceClientFactory
{
private static readonly JsonSerializerOptions JsonSerializerOptions = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
PropertyNameCaseInsensitive = true
};
private static readonly RefitSettings RefitSettings = new(new SystemTextJsonContentSerializer(JsonSerializerOptions));
public static T GetClient<T>(HttpClient client)
{
return RestService.For<T>(client, RefitSettings);
}
public static T GetClient<T>(string baseUrl)
{
var client = new HttpClient();
client.BaseAddress = new Uri(baseUrl);
return RestService.For<T>(client, RefitSettings);
}
private static HttpClient GetAuthorizedClient()
{
var httpClient = new HttpClient();
var jwtToken = ApiTokenHelper.GetAdminUserToken();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwtToken);
return httpClient;
}
}
}

View File

@ -1,25 +0,0 @@
using Persistence.Models;
using Refit;
namespace Persistence.IntegrationTests.Clients
{
/// <summary>
/// Интерфейс для тестирования API, предназначенного для работы с уставками
/// </summary>
public interface ISetpointClient
{
private const string BaseRoute = "/api/setpoint";
[Get($"{BaseRoute}/current")]
Task<IApiResponse<IEnumerable<SetpointValueDto>>> GetCurrent([Query(CollectionFormat.Multi)] IEnumerable<Guid> setpointKeys);
[Get($"{BaseRoute}/history")]
Task<IApiResponse<IEnumerable<SetpointValueDto>>> GetHistory([Query(CollectionFormat.Multi)] IEnumerable<Guid> setpointKeys, [Query] DateTimeOffset historyMoment);
[Get($"{BaseRoute}/log")]
Task<IApiResponse<Dictionary<Guid, IEnumerable<SetpointLogDto>>>> GetLog([Query(CollectionFormat.Multi)] IEnumerable<Guid> setpointKeys);
[Post($"{BaseRoute}/")]
Task<IApiResponse> Save(Guid setpointKey, object newValue);
}
}

View File

@ -1,4 +1,5 @@
using Persistence.Database.Model; using Persistence.Client;
using Persistence.Database.Model;
using Persistence.Repository.Data; using Persistence.Repository.Data;
using Xunit; using Xunit;
@ -11,7 +12,7 @@ public class DataSaubControllerTest : TimeSeriesBaseControllerTest<DataSaub, Dat
BitDepth = 2, BitDepth = 2,
BlockPosition = 3, BlockPosition = 3,
BlockSpeed = 4, BlockSpeed = 4,
Date = DateTimeOffset.Now, Date = DateTimeOffset.UtcNow,
Flow = 5, Flow = 5,
HookWeight = 6, HookWeight = 6,
Id = 7, Id = 7,

View File

@ -1,12 +1,14 @@
using System.Net; using System.Net;
using Persistence.IntegrationTests.Clients; using Microsoft.Extensions.DependencyInjection;
using Persistence.Client;
using Persistence.Client.Clients;
using Xunit; using Xunit;
namespace Persistence.IntegrationTests.Controllers namespace Persistence.IntegrationTests.Controllers
{ {
public class SetpointControllerTest : BaseIntegrationTest public class SetpointControllerTest : BaseIntegrationTest
{ {
private ISetpointClient client; private ISetpointClient setpointClient;
private class TestObject private class TestObject
{ {
public string? value1 { get; set; } public string? value1 { get; set; }
@ -14,7 +16,12 @@ namespace Persistence.IntegrationTests.Controllers
} }
public SetpointControllerTest(WebAppFactoryFixture factory) : base(factory) public SetpointControllerTest(WebAppFactoryFixture factory) : base(factory)
{ {
client = factory.GetHttpClient<ISetpointClient>(string.Empty); var scope = factory.Services.CreateScope();
var httpClient = scope.ServiceProvider
.GetRequiredService<IHttpClientFactory>()
.CreateClient();
setpointClient = PersistenceClientFactory.GetClient<ISetpointClient>(httpClient);
} }
[Fact] [Fact]
@ -28,7 +35,7 @@ namespace Persistence.IntegrationTests.Controllers
}; };
//act //act
var response = await client.GetCurrent(setpointKeys); var response = await setpointClient.GetCurrent(setpointKeys);
//assert //assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
@ -43,7 +50,7 @@ namespace Persistence.IntegrationTests.Controllers
var setpointKey = await Save(); var setpointKey = await Save();
//act //act
var response = await client.GetCurrent([setpointKey]); var response = await setpointClient.GetCurrent([setpointKey]);
//assert //assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
@ -64,7 +71,7 @@ namespace Persistence.IntegrationTests.Controllers
var historyMoment = DateTimeOffset.UtcNow; var historyMoment = DateTimeOffset.UtcNow;
//act //act
var response = await client.GetHistory(setpointKeys, historyMoment); var response = await setpointClient.GetHistory(setpointKeys, historyMoment);
//assert //assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
@ -81,7 +88,7 @@ namespace Persistence.IntegrationTests.Controllers
historyMoment = historyMoment.AddDays(1); historyMoment = historyMoment.AddDays(1);
//act //act
var response = await client.GetHistory([setpointKey], historyMoment); var response = await setpointClient.GetHistory([setpointKey], historyMoment);
//assert //assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
@ -101,7 +108,7 @@ namespace Persistence.IntegrationTests.Controllers
}; };
//act //act
var response = await client.GetLog(setpointKeys); var response = await setpointClient.GetLog(setpointKeys);
//assert //assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
@ -116,7 +123,7 @@ namespace Persistence.IntegrationTests.Controllers
var setpointKey = await Save(); var setpointKey = await Save();
//act //act
var response = await client.GetLog([setpointKey]); var response = await setpointClient.GetLog([setpointKey]);
//assert //assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
@ -142,7 +149,7 @@ namespace Persistence.IntegrationTests.Controllers
}; };
//act //act
var response = await client.Save(setpointKey, setpointValue); var response = await setpointClient.Save(setpointKey, setpointValue);
//assert //assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);

View File

@ -1,15 +1,8 @@
using Mapster;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration.UserSecrets;
using Persistence.IntegrationTests.Clients;
using Persistence.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net; using System.Net;
using System.Text; using Mapster;
using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection;
using Persistence.Client;
using Persistence.Client.Clients;
using Xunit; using Xunit;
namespace Persistence.IntegrationTests.Controllers; namespace Persistence.IntegrationTests.Controllers;
@ -17,13 +10,18 @@ public abstract class TimeSeriesBaseControllerTest<TEntity, TDto> : BaseIntegrat
where TEntity : class where TEntity : class
where TDto : class, new() where TDto : class, new()
{ {
private ITimeSeriesClient<TDto> client; private ITimeSeriesClient<TDto> timeSeriesClient;
public TimeSeriesBaseControllerTest(WebAppFactoryFixture factory) : base(factory) public TimeSeriesBaseControllerTest(WebAppFactoryFixture factory) : base(factory)
{ {
dbContext.CleanupDbSet<TEntity>(); dbContext.CleanupDbSet<TEntity>();
client = factory.GetAuthorizedHttpClient<ITimeSeriesClient<TDto>>(string.Empty); var scope = factory.Services.CreateScope();
var httpClient = scope.ServiceProvider
.GetRequiredService<IHttpClientFactory>()
.CreateClient();
timeSeriesClient = PersistenceClientFactory.GetClient<ITimeSeriesClient<TDto>>(httpClient);
} }
public async Task InsertRangeSuccess(TDto dto) public async Task InsertRangeSuccess(TDto dto)
@ -32,7 +30,7 @@ public abstract class TimeSeriesBaseControllerTest<TEntity, TDto> : BaseIntegrat
var expected = dto.Adapt<TDto>(); var expected = dto.Adapt<TDto>();
//act //act
var response = await client.InsertRangeAsync(new TDto[] { expected }); var response = await timeSeriesClient.InsertRangeAsync(new TDto[] { expected });
//assert //assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
@ -48,7 +46,7 @@ public abstract class TimeSeriesBaseControllerTest<TEntity, TDto> : BaseIntegrat
dbContext.SaveChanges(); dbContext.SaveChanges();
var response = await client.GetAsync(beginDate, endDate); var response = await timeSeriesClient.GetAsync(beginDate, endDate);
//assert //assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);

View File

@ -0,0 +1,8 @@
using System.Text.Json.Serialization;
namespace Persistence.IntegrationTests;
public class JwtToken
{
[JsonPropertyName("access_token")]
public required string AccessToken { get; set; }
}

View File

@ -0,0 +1,27 @@
namespace Persistence.IntegrationTests;
/// <summary>
/// настройки credentials для пользователя в KeyCloak
/// </summary>
public class KeyCloakUser
{
/// <summary>
///
/// </summary>
public required string Username { get; set; }
/// <summary>
///
/// </summary>
public required string Password { get; set; }
/// <summary>
///
/// </summary>
public required string ClientId { get; set; }
/// <summary>
///
/// </summary>
public required string GrantType { get; set; }
}

View File

@ -15,6 +15,7 @@
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="Refit" Version="8.0.0" /> <PackageReference Include="Refit" Version="8.0.0" />
<PackageReference Include="RestSharp" Version="112.1.0" />
<PackageReference Include="xunit" Version="2.9.2" /> <PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2"> <PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
@ -24,6 +25,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Persistence.API\Persistence.API.csproj" /> <ProjectReference Include="..\Persistence.API\Persistence.API.csproj" />
<ProjectReference Include="..\Persistence.Client\Persistence.Client.csproj" />
<ProjectReference Include="..\Persistence.Database.Postgres\Persistence.Database.Postgres.csproj" /> <ProjectReference Include="..\Persistence.Database.Postgres\Persistence.Database.Postgres.csproj" />
</ItemGroup> </ItemGroup>

View File

@ -0,0 +1,16 @@
namespace Persistence.IntegrationTests
{
public class TestHttpClientFactory : IHttpClientFactory
{
private readonly WebAppFactoryFixture factory;
public TestHttpClientFactory(WebAppFactoryFixture factory)
{
this.factory = factory;
}
public HttpClient CreateClient(string name)
{
return factory.CreateClient();
}
}
}

View File

@ -3,13 +3,17 @@ using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Persistence.API;
using Persistence.Database.Model; using Persistence.Database.Model;
using Persistence.Database.Postgres; using Persistence.Database.Postgres;
using Persistence.API;
using Refit; using Refit;
using RestSharp;
using System.Net.Http.Headers;
using System.Text.Json; using System.Text.Json;
using Persistence.Database.Postgres; using Persistence.Database.Postgres;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using Persistence.Client;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Persistence.IntegrationTests; namespace Persistence.IntegrationTests;
public class WebAppFactoryFixture : WebApplicationFactory<Startup> public class WebAppFactoryFixture : WebApplicationFactory<Startup>
@ -24,6 +28,8 @@ public class WebAppFactoryFixture : WebApplicationFactory<Startup>
private static readonly RefitSettings RefitSettings = new(new SystemTextJsonContentSerializer(JsonSerializerOptions)); private static readonly RefitSettings RefitSettings = new(new SystemTextJsonContentSerializer(JsonSerializerOptions));
private readonly string connectionString; private readonly string connectionString;
private readonly KeyCloakUser keycloakTestUser;
public readonly string KeycloakGetTokenUrl;
public WebAppFactoryFixture() public WebAppFactoryFixture()
{ {
@ -33,6 +39,10 @@ public class WebAppFactoryFixture : WebApplicationFactory<Startup>
var dbConnection = configuration.GetSection("DbConnection").Get<DbConnection>()!; var dbConnection = configuration.GetSection("DbConnection").Get<DbConnection>()!;
connectionString = dbConnection.GetConnectionString(); connectionString = dbConnection.GetConnectionString();
keycloakTestUser = configuration.GetSection("KeycloakTestUser").Get<KeyCloakUser>()!;
KeycloakGetTokenUrl = configuration.GetSection("KeycloakGetTokenUrl").Value!;
} }
protected override void ConfigureWebHost(IWebHostBuilder builder) protected override void ConfigureWebHost(IWebHostBuilder builder)
@ -47,14 +57,19 @@ public class WebAppFactoryFixture : WebApplicationFactory<Startup>
services.AddDbContext<PersistenceDbContext>(options => services.AddDbContext<PersistenceDbContext>(options =>
options.UseNpgsql(connectionString)); options.UseNpgsql(connectionString));
var serviceProvider = services.BuildServiceProvider(); services.RemoveAll<IHttpClientFactory>();
services.AddSingleton<IHttpClientFactory>(provider =>
{
return new TestHttpClientFactory(this);
});
var serviceProvider = services.BuildServiceProvider();
using var scope = serviceProvider.CreateScope(); using var scope = serviceProvider.CreateScope();
var scopedServices = scope.ServiceProvider; var scopedServices = scope.ServiceProvider;
var dbContext = scopedServices.GetRequiredService<PersistenceDbContext>();
var dbContext = scopedServices.GetRequiredService<PersistenceDbContext>();
dbContext.Database.EnsureCreatedAndMigrated(); dbContext.Database.EnsureCreatedAndMigrated();
//dbContext.Deposits.AddRange(Data.Defaults.Deposits);
dbContext.SaveChanges(); dbContext.SaveChanges();
}); });
} }
@ -81,9 +96,9 @@ public class WebAppFactoryFixture : WebApplicationFactory<Startup>
return RestService.For<T>(httpClient, RefitSettings); return RestService.For<T>(httpClient, RefitSettings);
} }
public T GetAuthorizedHttpClient<T>(string uriSuffix) public async Task<T> GetAuthorizedHttpClient<T>(string uriSuffix)
{ {
var httpClient = GetAuthorizedHttpClient(); var httpClient = await GetAuthorizedHttpClient();
if (string.IsNullOrEmpty(uriSuffix)) if (string.IsNullOrEmpty(uriSuffix))
return RestService.For<T>(httpClient, RefitSettings); return RestService.For<T>(httpClient, RefitSettings);
@ -93,11 +108,32 @@ public class WebAppFactoryFixture : WebApplicationFactory<Startup>
return RestService.For<T>(httpClient, RefitSettings); return RestService.For<T>(httpClient, RefitSettings);
} }
private HttpClient GetAuthorizedHttpClient() private async Task<HttpClient> GetAuthorizedHttpClient()
{ {
var httpClient = CreateClient(); var httpClient = CreateClient();
////var jwtToken = ApiTokenHelper.GetAdminUserToken(); var token = await GetTokenAsync();
//httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwtToken); httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
return httpClient; return httpClient;
} }
private async Task<string> GetTokenAsync()
{
var restClient = new RestClient();
var request = new RestRequest(KeycloakGetTokenUrl, Method.Post);
request.AddParameter("username", keycloakTestUser.Username);
request.AddParameter("password", keycloakTestUser.Password);
request.AddParameter("client_id", keycloakTestUser.ClientId);
request.AddParameter("grant_type", keycloakTestUser.GrantType);
var keyCloackResponse = await restClient.PostAsync(request);
if (keyCloackResponse.IsSuccessful && !String.IsNullOrEmpty(keyCloackResponse.Content))
{
var token = JsonSerializer.Deserialize<JwtToken>(keyCloackResponse.Content)!;
return token.AccessToken;
}
return String.Empty;
}
} }

View File

@ -13,7 +13,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Persistence.Database", "Per
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Persistence.IntegrationTests", "Persistence.IntegrationTests\Persistence.IntegrationTests.csproj", "{10752C25-3773-4081-A1F2-215A1D950126}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Persistence.IntegrationTests", "Persistence.IntegrationTests\Persistence.IntegrationTests.csproj", "{10752C25-3773-4081-A1F2-215A1D950126}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Persistence.Database.Postgres", "Persistence.Database.Postgres\Persistence.Database.Postgres.csproj", "{CC284D27-162D-490C-B6CF-74D666B7C5F3}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Persistence.Database.Postgres", "Persistence.Database.Postgres\Persistence.Database.Postgres.csproj", "{CC284D27-162D-490C-B6CF-74D666B7C5F3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Persistence.Client", "Persistence.Client\Persistence.Client.csproj", "{84B68660-48E6-4974-A4E5-517552D9DE23}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -45,6 +47,10 @@ Global
{CC284D27-162D-490C-B6CF-74D666B7C5F3}.Debug|Any CPU.Build.0 = Debug|Any CPU {CC284D27-162D-490C-B6CF-74D666B7C5F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CC284D27-162D-490C-B6CF-74D666B7C5F3}.Release|Any CPU.ActiveCfg = Release|Any CPU {CC284D27-162D-490C-B6CF-74D666B7C5F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CC284D27-162D-490C-B6CF-74D666B7C5F3}.Release|Any CPU.Build.0 = Release|Any CPU {CC284D27-162D-490C-B6CF-74D666B7C5F3}.Release|Any CPU.Build.0 = Release|Any CPU
{84B68660-48E6-4974-A4E5-517552D9DE23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{84B68660-48E6-4974-A4E5-517552D9DE23}.Debug|Any CPU.Build.0 = Debug|Any CPU
{84B68660-48E6-4974-A4E5-517552D9DE23}.Release|Any CPU.ActiveCfg = Release|Any CPU
{84B68660-48E6-4974-A4E5-517552D9DE23}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE