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

added cosmosdb sample #321

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
eaf669f
added cosmosdb sample
bradygaster Jun 13, 2024
9fcea51
Update samples/AspireWithCosmosDb/README.md
bradygaster Jun 14, 2024
9862c1f
updated image per feedback
bradygaster Jun 14, 2024
40eec45
Merge branch 'bradyg/cosmosdb' of https://github.com/dotnet/aspire-sa…
bradygaster Jun 14, 2024
72fa8f9
Update samples/AspireWithCosmosDb/AspireWithCosmos.ApiService/Program.cs
bradygaster Jun 14, 2024
bc8d579
Update samples/AspireWithCosmosDb/AspireWithCosmos.AppHost/AspireWith…
bradygaster Jun 14, 2024
c04b9e3
formatting
bradygaster Jun 14, 2024
d3d82be
Merge branch 'bradyg/cosmosdb' of https://github.com/dotnet/aspire-sa…
bradygaster Jun 14, 2024
1883ca0
more formatting per the ide's suggestions
bradygaster Jun 14, 2024
4637d7e
Update samples/AspireWithCosmosDb/AspireWithCosmos.Web/TodoApiClient.cs
bradygaster Jun 14, 2024
0a0d082
Update samples/AspireWithCosmosDb/AspireWithCosmos.Tests/AspireWithCo…
bradygaster Jun 14, 2024
c909987
Update samples/AspireWithCosmosDb/AspireWithCosmos.ServiceDefaults/As…
bradygaster Jun 14, 2024
4d22886
Update samples/AspireWithCosmosDb/AspireWithCosmos.ServiceDefaults/As…
bradygaster Jun 14, 2024
48a4de5
removed test project per feedback.
bradygaster Jun 14, 2024
f91372c
Merge branch 'bradyg/cosmosdb' of https://github.com/dotnet/aspire-sa…
bradygaster Jun 14, 2024
ec5f76e
simplified terminal image
bradygaster Jun 14, 2024
789446c
added emulator call and comment.
bradygaster Jun 14, 2024
ba4f8bb
added cosmos db to tests
bradygaster Jun 19, 2024
339c7d4
updated an otel lib
bradygaster Jun 21, 2024
c349ac1
one more tweak
bradygaster Jun 21, 2024
1066a07
Bump packages & other tweaks
DamianEdwards Jun 21, 2024
c035293
Add retry policy & health check for Cosmos DB db creation
DamianEdwards Jun 21, 2024
d27bf16
Update samples/AspireWithCosmosDb/AspireWithCosmos.ApiService/AspireW…
bradygaster Jun 25, 2024
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,19 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

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

<ItemGroup>
<PackageReference Include="Aspire.Microsoft.Azure.Cosmos" Version="8.0.1" />
<PackageReference Include="NSwag.AspNetCore" Version="14.0.7" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.5" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\AspireWithCosmos.ServiceDefaults\AspireWithCosmos.ServiceDefaults.csproj" />
</ItemGroup>

</Project>
78 changes: 78 additions & 0 deletions samples/AspireWithCosmosDb/AspireWithCosmos.ApiService/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using Microsoft.Azure.Cosmos;

var builder = WebApplication.CreateBuilder(args);

builder.AddServiceDefaults();
builder.Services.AddProblemDetails();
builder.Services.AddHostedService<DatabaseBootstrapper>();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddOpenApiDocument();
builder.AddAzureCosmosClient("cosmos");

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
app.UseOpenApi();
app.UseSwaggerUi();
}

app.UseExceptionHandler();

// create new todos
app.MapPost("/todos", async (Todo todo, CosmosClient cosmosClient) =>
(await cosmosClient.GetAppDataContainer().CreateItemAsync<Todo>(todo)).Resource
);

// get all the todos
app.MapGet("/todos", (CosmosClient cosmosClient) =>
cosmosClient.GetAppDataContainer().GetItemLinqQueryable<Todo>(allowSynchronousQueryExecution: true).ToList()
);

app.MapPut("/todos/{id}", async (string id, Todo todo, CosmosClient cosmosClient) =>
(await cosmosClient.GetAppDataContainer().ReplaceItemAsync<Todo>(todo, id)).Resource
);

app.MapDelete("/todos/{userId}/{id}", async (string userId, string id, CosmosClient cosmosClient) =>
{
await cosmosClient.GetAppDataContainer().DeleteItemAsync<Todo>(id, new PartitionKey(userId));
return Results.Ok();
});

app.MapDefaultEndpoints();

app.Run();

// The Todo service model used for transmitting data
public record Todo(string Description, string id, string UserId, bool IsComplete = false)
{
// partiion the todos by user id
internal static string UserIdPartitionKey = "/UserId";
}

// Background service used to scaffold the Cosmos DB/Container
public class DatabaseBootstrapper(CosmosClient cosmosClient) : IHostedService
{
public async Task StartAsync(CancellationToken cancellationToken)
{
await cosmosClient.CreateDatabaseIfNotExistsAsync("tododb");
var database = cosmosClient.GetDatabase("tododb");
await database.CreateContainerIfNotExistsAsync(new ContainerProperties("todos", Todo.UserIdPartitionKey));
}

public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
bradygaster marked this conversation as resolved.
Show resolved Hide resolved
}

// Convenience class for reusing boilerplate code
public static class CosmosClientTodoAppExtensions
{
public static Container GetAppDataContainer(this CosmosClient cosmosClient)
{
var database = cosmosClient.GetDatabase("tododb");
var todos = database.GetContainer("todos");

if(todos == null) throw new ApplicationException("Cosmos DB collection missing.");
bradygaster marked this conversation as resolved.
Show resolved Hide resolved

return todos;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5535",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7422;http://localhost:5535",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">

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

<ItemGroup>
<ProjectReference Include="..\AspireWithCosmos.ApiService\AspireWithCosmos.ApiService.csproj" />
<ProjectReference Include="..\AspireWithCosmos.Web\AspireWithCosmos.Web.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Aspire.Hosting.AppHost" Version="8.0.0" />
bradygaster marked this conversation as resolved.
Show resolved Hide resolved
<PackageReference Include="Aspire.Hosting.Azure.CosmosDB" Version="8.0.1" />
</ItemGroup>

</Project>
12 changes: 12 additions & 0 deletions samples/AspireWithCosmosDb/AspireWithCosmos.AppHost/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
var builder = DistributedApplication.CreateBuilder(args);

var cosmos = builder.AddAzureCosmosDB("cosmos");
bradygaster marked this conversation as resolved.
Show resolved Hide resolved

var apiService = builder.AddProject<Projects.AspireWithCosmos_ApiService>("apiservice")
.WithReference(cosmos);

builder.AddProject<Projects.AspireWithCosmos_Web>("webfrontend")
.WithExternalHttpEndpoints()
.WithReference(apiService);
bradygaster marked this conversation as resolved.
Show resolved Hide resolved

builder.Build().Run();
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:17097;http://localhost:15184",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development",
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21143",
"DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22152"
}
},
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:15184",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development",
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19196",
"DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20015"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Aspire.Hosting.Dcp": "Warning"
}
}
}
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="8.3.0" />
bradygaster marked this conversation as resolved.
Show resolved Hide resolved
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="8.0.0" />
bradygaster marked this conversation as resolved.
Show resolved Hide resolved
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.8.1" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.8.1" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.8.1" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.8.1" />
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.8.0" />
bradygaster marked this conversation as resolved.
Show resolved Hide resolved
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Logging;
using OpenTelemetry;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;

namespace Microsoft.Extensions.Hosting;

// Adds common .NET Aspire services: service discovery, resilience, health checks, and OpenTelemetry.
// This project should be referenced by each service project in your solution.
// To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults
public static class Extensions
{
public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBuilder builder)
{
builder.ConfigureOpenTelemetry();

builder.AddDefaultHealthChecks();

builder.Services.AddServiceDiscovery();

builder.Services.ConfigureHttpClientDefaults(http =>
{
// Turn on resilience by default
http.AddStandardResilienceHandler();

// Turn on service discovery by default
http.AddServiceDiscovery();
});

return builder;
}

public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicationBuilder builder)
{
builder.Logging.AddOpenTelemetry(logging =>
{
logging.IncludeFormattedMessage = true;
logging.IncludeScopes = true;
});

builder.Services.AddOpenTelemetry()
.WithMetrics(metrics =>
{
metrics.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddRuntimeInstrumentation();
})
.WithTracing(tracing =>
{
tracing.AddAspNetCoreInstrumentation()
// Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package)
//.AddGrpcClientInstrumentation()
bradygaster marked this conversation as resolved.
Show resolved Hide resolved
.AddHttpClientInstrumentation();
});

builder.AddOpenTelemetryExporters();

return builder;
}

private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostApplicationBuilder builder)
{
var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);

if (useOtlpExporter)
{
builder.Services.AddOpenTelemetry().UseOtlpExporter();
}

// Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package)
//if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"]))
//{
// builder.Services.AddOpenTelemetry()
// .UseAzureMonitor();
//}
bradygaster marked this conversation as resolved.
Show resolved Hide resolved

return builder;
}

public static IHostApplicationBuilder AddDefaultHealthChecks(this IHostApplicationBuilder builder)
{
builder.Services.AddHealthChecks()
// Add a default liveness check to ensure app is responsive
.AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]);

return builder;
}

public static WebApplication MapDefaultEndpoints(this WebApplication app)
{
// Adding health checks endpoints to applications in non-development environments has security implications.
// See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments.
if (app.Environment.IsDevelopment())
{
// All health checks must pass for app to be considered ready to accept traffic after starting
app.MapHealthChecks("/health");

// Only health checks tagged with the "live" tag must pass for app to be considered alive
app.MapHealthChecks("/alive", new HealthCheckOptions
{
Predicate = r => r.Tags.Contains("live")
});
}

return app;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<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="8.0.0" />
bradygaster marked this conversation as resolved.
Show resolved Hide resolved
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
bradygaster marked this conversation as resolved.
Show resolved Hide resolved
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\AspireWithCosmos.AppHost\AspireWithCosmos.AppHost.csproj" />
</ItemGroup>

<ItemGroup>
<Using Include="Aspire.Hosting.Testing" />
<Using Include="Xunit" />
</ItemGroup>

</Project>
Loading
Loading