Skip to content
Open
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
33 changes: 33 additions & 0 deletions Aspire.AppHost.Tests/Aspire.AppHost.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">

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

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Aspire.Hosting.Testing" Version="9.5.0" />
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="MartinCostello.Logging.XUnit" Version="0.7.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Aspire\Aspire.AppHost\Aspire.AppHost.csproj" />
<ProjectReference Include="..\Service.Api\Service.Api.csproj" />
</ItemGroup>

<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>

</Project>
77 changes: 77 additions & 0 deletions Aspire.AppHost.Tests/UnitTest1.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using System.Text.Json;
using Aspire.Hosting;
using Aspire.Hosting.Testing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Service.Api.Entity;
using Xunit.Abstractions;
using System.Net.Http.Json;

namespace Aspire.AppHost.Tests;

public class UnitTest1(ITestOutputHelper output) : IAsyncLifetime
{
private IDistributedApplicationTestingBuilder? _builder;
private DistributedApplication? _app;

/// <inheritdoc/>
public async Task InitializeAsync()
{
var cancellationToken = CancellationToken.None;
_builder = await DistributedApplicationTestingBuilder.CreateAsync<Projects.Aspire_AppHost>(cancellationToken);
_builder.Configuration["DcpPublisher:RandomizePorts"] = "false";
_builder.Services.AddLogging(logging =>
{
logging.AddXUnit(output);
logging.SetMinimumLevel(LogLevel.Debug);
logging.AddFilter("Aspire.Hosting.Dcp", LogLevel.Debug);
logging.AddFilter("Aspire.Hosting", LogLevel.Debug);
});
}

[Theory]
[InlineData("Development")]
public async Task TestPipeline(string envName)
{
var cancellationToken = CancellationToken.None;
_builder!.Environment.EnvironmentName = envName;
_app = await _builder.BuildAsync(cancellationToken);
await _app.StartAsync(cancellationToken);

var random = new Random();
var id = random.Next(1, 100);
using var gatewayClient = _app.CreateHttpClient("api-gw", "http");
using var gatewayResponse = await gatewayClient!.GetAsync($"/api/projects?id={id}");
var api = await gatewayResponse.Content.ReadFromJsonAsync<ProgramProject>(cancellationToken: cancellationToken);

await Task.Delay(5000);
using var storageClient = _app.CreateHttpClient("programproj-storage", "http");
using var listResponse = await storageClient!.GetAsync($"/api/s3");
var ppList = await listResponse.Content.ReadFromJsonAsync<List<string>>(cancellationToken: cancellationToken);
using var s3Response = await storageClient!.GetAsync($"/api/s3/programproj_{id}.json");
var s3 = await s3Response.Content.ReadFromJsonAsync<ProgramProject>(cancellationToken: cancellationToken);

Assert.NotNull(ppList);
Assert.Single(ppList);
Assert.NotNull(api);
Assert.NotNull(s3);
Assert.Equal(id, s3.Id);
Assert.Equivalent(api, s3);
}

/// <inheritdoc/>
public async Task DisposeAsync()
{
if (_app is not null)
{
await _app.StopAsync();
await _app.DisposeAsync();
}

if (_builder is not null)
{
await _builder.DisposeAsync();
}
}

}
85 changes: 85 additions & 0 deletions Aspire/Aspire.AppHost/AppHost.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using Amazon;
using Microsoft.Extensions.Configuration;
using Aspire.Hosting.LocalStack.Container;
using Projects;

var builder = DistributedApplication.CreateBuilder(args);

var cache = builder.AddRedis("programproj-cache").WithRedisInsight(containerName: "programproj-insight");

var ports = builder.Configuration.GetSection("ApiGateway:Ports").Get<int[]>()
?? throw new InvalidOperationException("Configuration section 'ApiGateway:Ports' is missing or empty in appsettings.json file.");

var apiGW = builder.AddProject<Projects.Service_Gateway>("api-gw", project =>
{
project.ExcludeLaunchProfile = true;
})
.WithHttpEndpoint(port: 5247)
.WithHttpsEndpoint(port: 7198);

var awsConfig = builder.AddAWSSDKConfig()
.WithProfile("default")
.WithRegion(RegionEndpoint.EUCentral1);

var localstack = builder.AddLocalStack("programproj-localstack", awsConfig: awsConfig, configureContainer: container =>
{
container.Lifetime = ContainerLifetime.Session;
container.DebugLevel = 1;
container.LogLevel = LocalStackLogLevel.Debug;
container.Port = 4566;
container.AdditionalEnvironmentVariables.Add("DEBUG", "1");
container.AdditionalEnvironmentVariables.Add("SNS_CERT_URL_HOST", "sns.eu-central-1.amazonaws.com");
});

var templatePath = Path.Combine(
builder.AppHostDirectory,
"Cloudformation",
"programproj-template-sns.yml");

var awsResources = builder.AddAWSCloudFormationTemplate(
"resources",
templatePath,
"programproj")
.WithReference(awsConfig);

var storage = builder.AddProject<Projects.Service_Storage>("programproj-storage", project =>
{
project.ExcludeLaunchProfile = true;
})
.WithReference(awsResources)
.WithHttpEndpoint(port: 5280)
.WithEnvironment("AWS__Resources__SNSUrl", "http://host.docker.internal:5280/api/sns")
.WaitFor(awsResources);

var minio = builder.AddMinioContainer("programproj-minio");

storage.WithEnvironment("AWS__Resources__MinioBucketName", "programproj-bucket")
.WithReference(minio)
.WaitFor(minio);

for (var i = 0; i < ports.Length; i++)
{
var httpsPort = ports[i];
var httpPort = httpsPort - 1000;

var service = builder.AddProject<Projects.Service_Api>($"programproj-api{i + 1}", project =>
{
project.ExcludeLaunchProfile = true;
})
.WithReference(cache, "RedisCache")
.WithHttpEndpoint(port: httpPort)
.WithHttpsEndpoint(port: httpsPort)
.WithReference(awsResources)
.WithHttpHealthCheck("/health", endpointName: "https")
.WaitFor(cache)
.WaitFor(awsResources);

apiGW.WaitFor(service);
}

builder.AddProject<Projects.Client_Wasm>("programproj-wasm")
.WaitFor(apiGW);

builder.UseLocalStack(localstack);

builder.Build().Run();
31 changes: 31 additions & 0 deletions Aspire/Aspire.AppHost/Aspire.AppHost.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">

<Sdk Name="Aspire.AppHost.Sdk" Version="9.5.0" />

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UserSecretsId>348a6352-668a-4b0d-833f-bbd1cc51ceb3</UserSecretsId>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\Client.Wasm\Client.Wasm.csproj" />
<ProjectReference Include="..\..\Service.Api\Service.Api.csproj" />
<ProjectReference Include="..\..\Service.Gateway\Service.Gateway.csproj" />
<ProjectReference Include="..\..\Service.Storage\Service.Storage.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Aspire.Hosting.AppHost" Version="9.5.0" />
<PackageReference Include="Aspire.Hosting.Redis" Version="9.5.0" />
<PackageReference Include="CommunityToolkit.Aspire.Hosting.Minio" Version="9.9.0" />
<PackageReference Include="LocalStack.Aspire.Hosting" Version="9.5.3" />
</ItemGroup>

<ItemGroup>
<Folder Include="Cloudformation\" />
</ItemGroup>

</Project>
29 changes: 29 additions & 0 deletions Aspire/Aspire.AppHost/Cloudformation/programproj-template-sns.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Cloud formation template for program-project project'

Parameters:
TopicName:
Type: String
Description: Name for the SNS topic
Default: 'programproj-topic'

Resources:
ProgramProjTopic:
Type: AWS::SNS::Topic
Properties:
TopicName: !Ref TopicName
DisplayName: !Ref TopicName
Tags:
- Key: Name
Value: !Ref TopicName
- Key: Environment
Value: Sample

Outputs:
SNSTopicName:
Description: Name of the SNS topic
Value: !GetAtt ProgramProjTopic.TopicName

SNSTopicArn:
Description: ARN of the SNS topic
Value: !Ref ProgramProjTopic
29 changes: 29 additions & 0 deletions Aspire/Aspire.AppHost/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:17161;http://localhost:15041",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development",
"ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21204",
"ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22223"
}
},
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:15041",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development",
"ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19104",
"ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20068"
}
}
}
}
8 changes: 8 additions & 0 deletions Aspire/Aspire.AppHost/appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
15 changes: 15 additions & 0 deletions Aspire/Aspire.AppHost/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Aspire.Hosting.Dcp": "Warning"
}
},
"ApiGateway": {
"Ports": [ 4441, 4442, 4443, 4444, 4445 ]
},
"LocalStack": {
"UseLocalStack": true
}
}
22 changes: 22 additions & 0 deletions Aspire/Aspire.ServiceDefaults/Aspire.ServiceDefaults.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsAspireSharedProject>true</IsAspireSharedProject>
</PropertyGroup>

<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />

<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="9.9.0" />
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="9.5.0" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.9.0" />
</ItemGroup>

</Project>
Loading