From 26a8c446c5a0bf578848cade5f2f22a026035aa7 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Thu, 18 Jan 2024 23:29:02 +1100 Subject: [PATCH 1/6] Cosmos playground. --- Aspire.sln | 26 ++++ .../AzureCosmosDBDatabaseResource.cs | 2 +- .../AspireAzureCosmosDBExtensions.cs | 14 ++ .../CosmosEndToEnd.ApiService.csproj | 15 +++ .../CosmosEndToEnd.ApiService.http | 6 + .../CosmosEndToEnd.ApiService/Program.cs | 70 ++++++++++ .../Properties/launchSettings.json | 14 ++ .../StjCosmosSerializer.cs | 45 +++++++ .../appsettings.Development.json | 8 ++ .../appsettings.json | 9 ++ .../CosmosEndToEnd.AppHost.csproj | 17 +++ .../Directory.Build.props | 8 ++ .../Directory.Build.targets | 8 ++ .../CosmosEndToEnd.AppHost/Program.cs | 13 ++ .../Properties/launchSettings.json | 28 ++++ .../appsettings.Development.json | 8 ++ .../CosmosEndToEnd.AppHost/appsettings.json | 9 ++ .../CosmosEndToEnd.ServiceDefaults.csproj | 27 ++++ .../Extensions.cs | 122 ++++++++++++++++++ 19 files changed, 448 insertions(+), 1 deletion(-) create mode 100644 src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/CosmosEndToEnd.ApiService.csproj create mode 100644 src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/CosmosEndToEnd.ApiService.http create mode 100644 src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Program.cs create mode 100644 src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Properties/launchSettings.json create mode 100644 src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/StjCosmosSerializer.cs create mode 100644 src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/appsettings.Development.json create mode 100644 src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/appsettings.json create mode 100644 src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/CosmosEndToEnd.AppHost.csproj create mode 100644 src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/Directory.Build.props create mode 100644 src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/Directory.Build.targets create mode 100644 src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/Program.cs create mode 100644 src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/Properties/launchSettings.json create mode 100644 src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/appsettings.Development.json create mode 100644 src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/appsettings.json create mode 100644 src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ServiceDefaults/CosmosEndToEnd.ServiceDefaults.csproj create mode 100644 src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ServiceDefaults/Extensions.cs diff --git a/Aspire.sln b/Aspire.sln index a4248b90cbf..16a58da4253 100644 --- a/Aspire.sln +++ b/Aspire.sln @@ -194,6 +194,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.Confluent.Kafka", "s EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.Confluent.Kafka.Tests", "tests\Aspire.Confluent.Kafka.Tests\Aspire.Confluent.Kafka.Tests.csproj", "{A8CB331A-1247-41D9-8118-538E5A2CC9DF}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Playgrounds", "Playgrounds", "{DF8ADBC2-5B15-422F-A703-C60711D5552D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CosmosEndToEnd", "CosmosEndToEnd", "{DBEDDF76-1C33-4943-8CCB-337A7D48AFF5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CosmosEndToEnd.AppHost", "src\Playgrounds\CosmosEndToEnd\CosmosEndToEnd.AppHost\CosmosEndToEnd.AppHost.csproj", "{51DDD6BC-1D6C-466A-B509-FC49E3BD72E4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CosmosEndToEnd.ServiceDefaults", "src\Playgrounds\CosmosEndToEnd\CosmosEndToEnd.ServiceDefaults\CosmosEndToEnd.ServiceDefaults.csproj", "{71A215C3-61E3-4E46-880C-286C794AD7B3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CosmosEndToEnd.ApiService", "src\Playgrounds\CosmosEndToEnd\CosmosEndToEnd.ApiService\CosmosEndToEnd.ApiService.csproj", "{EABB20A8-CDA2-4AFE-A5B1-FB631200CD64}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -520,6 +530,18 @@ Global {A8CB331A-1247-41D9-8118-538E5A2CC9DF}.Debug|Any CPU.Build.0 = Debug|Any CPU {A8CB331A-1247-41D9-8118-538E5A2CC9DF}.Release|Any CPU.ActiveCfg = Release|Any CPU {A8CB331A-1247-41D9-8118-538E5A2CC9DF}.Release|Any CPU.Build.0 = Release|Any CPU + {51DDD6BC-1D6C-466A-B509-FC49E3BD72E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {51DDD6BC-1D6C-466A-B509-FC49E3BD72E4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {51DDD6BC-1D6C-466A-B509-FC49E3BD72E4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {51DDD6BC-1D6C-466A-B509-FC49E3BD72E4}.Release|Any CPU.Build.0 = Release|Any CPU + {71A215C3-61E3-4E46-880C-286C794AD7B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {71A215C3-61E3-4E46-880C-286C794AD7B3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {71A215C3-61E3-4E46-880C-286C794AD7B3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {71A215C3-61E3-4E46-880C-286C794AD7B3}.Release|Any CPU.Build.0 = Release|Any CPU + {EABB20A8-CDA2-4AFE-A5B1-FB631200CD64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EABB20A8-CDA2-4AFE-A5B1-FB631200CD64}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EABB20A8-CDA2-4AFE-A5B1-FB631200CD64}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EABB20A8-CDA2-4AFE-A5B1-FB631200CD64}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -610,6 +632,10 @@ Global {F7D9FA54-1F64-4A36-961A-0087F8E88D07} = {8BAF2119-8370-4E9E-A887-D92506F8C727} {174E0507-3BB0-4CDC-829E-9CA75DA66473} = {27381127-6C45-4B4C-8F18-41FF48DFE4B2} {A8CB331A-1247-41D9-8118-538E5A2CC9DF} = {4981B3A5-4AFD-4191-BF7D-8692D9783D60} + {DBEDDF76-1C33-4943-8CCB-337A7D48AFF5} = {DF8ADBC2-5B15-422F-A703-C60711D5552D} + {51DDD6BC-1D6C-466A-B509-FC49E3BD72E4} = {DBEDDF76-1C33-4943-8CCB-337A7D48AFF5} + {71A215C3-61E3-4E46-880C-286C794AD7B3} = {DBEDDF76-1C33-4943-8CCB-337A7D48AFF5} + {EABB20A8-CDA2-4AFE-A5B1-FB631200CD64} = {DBEDDF76-1C33-4943-8CCB-337A7D48AFF5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6DCEDFEC-988E-4CB3-B45B-191EB5086E0C} diff --git a/src/Aspire.Hosting.Azure/AzureCosmosDBDatabaseResource.cs b/src/Aspire.Hosting.Azure/AzureCosmosDBDatabaseResource.cs index fafc43cc073..500d6c5a831 100644 --- a/src/Aspire.Hosting.Azure/AzureCosmosDBDatabaseResource.cs +++ b/src/Aspire.Hosting.Azure/AzureCosmosDBDatabaseResource.cs @@ -26,5 +26,5 @@ public AzureCosmosDBDatabaseResource(string name, AzureCosmosDBResource parent) /// Gets the connection string to use for this database. /// /// The connection string to use for this database. - public string? GetConnectionString() => ConnectionString; + public string? GetConnectionString() => ConnectionString ?? $"{Parent.GetConnectionString()}Database={Name};"; // HACK: Will go away when we get rid of Azure Provisioner package. } diff --git a/src/Components/Aspire.Microsoft.Azure.Cosmos/AspireAzureCosmosDBExtensions.cs b/src/Components/Aspire.Microsoft.Azure.Cosmos/AspireAzureCosmosDBExtensions.cs index 75548a9dad5..db784cd480e 100644 --- a/src/Components/Aspire.Microsoft.Azure.Cosmos/AspireAzureCosmosDBExtensions.cs +++ b/src/Components/Aspire.Microsoft.Azure.Cosmos/AspireAzureCosmosDBExtensions.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Data.Common; using Aspire.Hosting.Azure.Cosmos; using Aspire.Microsoft.Azure.Cosmos; using Azure.Identity; @@ -108,6 +109,19 @@ private static void AddAzureCosmosDB( if (serviceKey is null) { builder.Services.AddSingleton(_ => ConfigureDb()); + + var csBuilder = new DbConnectionStringBuilder(); + csBuilder.ConnectionString = settings.ConnectionString; + + if (csBuilder.TryGetValue("Database", out var databaseName)) + { + builder.Services.AddSingleton(sp => + { + var client = sp.GetRequiredService(); + var database = client.CreateDatabaseIfNotExistsAsync(databaseName.ToString()).Result.Database; + return database; + }); + } } else { diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/CosmosEndToEnd.ApiService.csproj b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/CosmosEndToEnd.ApiService.csproj new file mode 100644 index 00000000000..077dd67d8b2 --- /dev/null +++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/CosmosEndToEnd.ApiService.csproj @@ -0,0 +1,15 @@ + + + + net8.0 + enable + enable + true + + + + + + + + diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/CosmosEndToEnd.ApiService.http b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/CosmosEndToEnd.ApiService.http new file mode 100644 index 00000000000..ebb1db4fe79 --- /dev/null +++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/CosmosEndToEnd.ApiService.http @@ -0,0 +1,6 @@ +@CosmosEndToEnd.ApiService_HostAddress = http://localhost:5193 + +GET {{CosmosEndToEnd.ApiService_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Program.cs b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Program.cs new file mode 100644 index 00000000000..52c71acf8fa --- /dev/null +++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Program.cs @@ -0,0 +1,70 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text.Json.Serialization; +using CosmosEndToEnd.ApiService; +using Microsoft.Azure.Cosmos; + +var sessionId = Guid.NewGuid().ToString(); + +var builder = WebApplication.CreateBuilder(args); + +builder.AddServiceDefaults(); +builder.AddAzureCosmosDB("db", + settings => + { + settings.IgnoreEmulatorCertificate = true; + }, + clientOptions => + { + // Default serializer for Cosmos V3 client is JSON.NET, this changes + // us to use S.T.J for this playground. + clientOptions.Serializer = new StjSerializer(new System.Text.Json.JsonSerializerOptions()); + }); + +builder.Services.AddKeyedSingleton("entries", (sp, _) => +{ + var db = sp.GetRequiredService(); + var container = db.CreateContainerIfNotExistsAsync("entries", "/sessionId").Result.Container; + return container; +}); + +var app = builder.Build(); + +app.MapGet("/", async ([FromKeyedServices("entries")]Container container) => +{ + // Add an entry to the database on each request. + await container.CreateItemAsync(new Entry(Guid.NewGuid().ToString(), sessionId)).ConfigureAwait(false); + + var entries = new List(); + var iterator = container.GetItemQueryIterator(requestOptions: new QueryRequestOptions() { MaxItemCount = 5 }); + + var batchCount = 0; + while (iterator.HasMoreResults) + { + batchCount++; + var batch = await iterator.ReadNextAsync().ConfigureAwait(false); + foreach (var entry in batch) + { + entries.Add(entry); + } + } + + return new + { + batchCount = batchCount, + totalEntries = entries.Count, + entries = entries + }; +}); + +app.Run(); + +public class Entry(string id, string sessionId) +{ + [JsonPropertyName("id")] + public string Id { get; set; } = id; + + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = sessionId; +} diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Properties/launchSettings.json b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Properties/launchSettings.json new file mode 100644 index 00000000000..de23e4696cf --- /dev/null +++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Properties/launchSettings.json @@ -0,0 +1,14 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:5193", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/StjCosmosSerializer.cs b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/StjCosmosSerializer.cs new file mode 100644 index 00000000000..a4e3b64c4a6 --- /dev/null +++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/StjCosmosSerializer.cs @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text.Json; +using Azure.Core.Serialization; +using Microsoft.Azure.Cosmos; + +namespace CosmosEndToEnd.ApiService; + +public class StjSerializer: CosmosSerializer +{ + private readonly JsonObjectSerializer _systemTextJsonSerializer; + + public StjSerializer(JsonSerializerOptions jsonSerializerOptions) + { + this._systemTextJsonSerializer = new JsonObjectSerializer(jsonSerializerOptions); + } + + public override T FromStream(Stream stream) + { + using (stream) + { + if (stream.CanSeek + && stream.Length == 0) + { + return default!; + } + + if (typeof(Stream).IsAssignableFrom(typeof(T))) + { + return (T)(object)stream; + } + + return (T)this._systemTextJsonSerializer.Deserialize(stream, typeof(T), default)!; + } + } + + public override Stream ToStream(T input) + { + MemoryStream streamPayload = new MemoryStream(); + this._systemTextJsonSerializer.Serialize(streamPayload, input, input.GetType(), default); + streamPayload.Position = 0; + return streamPayload; + } +} diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/appsettings.Development.json b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/appsettings.Development.json new file mode 100644 index 00000000000..0c208ae9181 --- /dev/null +++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/appsettings.json b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/appsettings.json new file mode 100644 index 00000000000..10f68b8c8b4 --- /dev/null +++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/CosmosEndToEnd.AppHost.csproj b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/CosmosEndToEnd.AppHost.csproj new file mode 100644 index 00000000000..833f8567418 --- /dev/null +++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/CosmosEndToEnd.AppHost.csproj @@ -0,0 +1,17 @@ + + + + Exe + net8.0 + enable + enable + true + + + + + + + + + diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/Directory.Build.props b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/Directory.Build.props new file mode 100644 index 00000000000..dbc16a48c05 --- /dev/null +++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/Directory.Build.props @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/Directory.Build.targets b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/Directory.Build.targets new file mode 100644 index 00000000000..ff4a3c400b3 --- /dev/null +++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/Directory.Build.targets @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/Program.cs b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/Program.cs new file mode 100644 index 00000000000..bf20951d3a2 --- /dev/null +++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/Program.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +var builder = DistributedApplication.CreateBuilder(args); + +var db = builder.AddAzureCosmosDB("cosmos") + .UseEmulator() + .AddDatabase("db"); + +builder.AddProject("api") + .WithReference(db); + +builder.Build().Run(); diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/Properties/launchSettings.json b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/Properties/launchSettings.json new file mode 100644 index 00000000000..539615401c1 --- /dev/null +++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/Properties/launchSettings.json @@ -0,0 +1,28 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:15129", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16175" + } + }, + "generate-manifest": { + "commandName": "Project", + "launchBrowser": true, + "dotnetRunMessages": true, + "commandLineArgs": "--publisher manifest --output-path aspire-manifest.json", + "applicationUrl": "http://localhost:15129", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16175" + } + } + } +} diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/appsettings.Development.json b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/appsettings.Development.json new file mode 100644 index 00000000000..0c208ae9181 --- /dev/null +++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/appsettings.json b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/appsettings.json new file mode 100644 index 00000000000..31c092aa450 --- /dev/null +++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Aspire.Hosting.Dcp": "Warning" + } + } +} diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ServiceDefaults/CosmosEndToEnd.ServiceDefaults.csproj b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ServiceDefaults/CosmosEndToEnd.ServiceDefaults.csproj new file mode 100644 index 00000000000..07fade10e2f --- /dev/null +++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ServiceDefaults/CosmosEndToEnd.ServiceDefaults.csproj @@ -0,0 +1,27 @@ + + + + Library + net8.0 + enable + enable + true + + + + + + + + + + + + + + + + + + + diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ServiceDefaults/Extensions.cs b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ServiceDefaults/Extensions.cs new file mode 100644 index 00000000000..30ea4a2493f --- /dev/null +++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ServiceDefaults/Extensions.cs @@ -0,0 +1,122 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Logging; +using OpenTelemetry.Logs; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace Microsoft.Extensions.Hosting; + +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.UseServiceDiscovery(); + }); + + 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.AddRuntimeInstrumentation() + .AddBuiltInMeters(); + }) + .WithTracing(tracing => + { + if (builder.Environment.IsDevelopment()) + { + // We want to view all traces in development + tracing.SetSampler(new AlwaysOnSampler()); + } + + tracing.AddAspNetCoreInstrumentation() + .AddGrpcClientInstrumentation() + .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.Configure(logging => logging.AddOtlpExporter()); + builder.Services.ConfigureOpenTelemetryMeterProvider(metrics => metrics.AddOtlpExporter()); + builder.Services.ConfigureOpenTelemetryTracerProvider(tracing => tracing.AddOtlpExporter()); + } + + // Uncomment the following lines to enable the Prometheus exporter (requires the OpenTelemetry.Exporter.Prometheus.AspNetCore package) + // builder.Services.AddOpenTelemetry() + // .WithMetrics(metrics => metrics.AddPrometheusExporter()); + + // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.Exporter package) + // builder.Services.AddOpenTelemetry() + // .UseAzureMonitor(); + + 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) + { + // Uncomment the following line to enable the Prometheus endpoint (requires the OpenTelemetry.Exporter.Prometheus.AspNetCore package) + // app.MapPrometheusScrapingEndpoint(); + + // 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; + } + + private static MeterProviderBuilder AddBuiltInMeters(this MeterProviderBuilder meterProviderBuilder) => + meterProviderBuilder.AddMeter( + "Microsoft.AspNetCore.Hosting", + "Microsoft.AspNetCore.Server.Kestrel", + "System.Net.Http"); +} From e4e706c6d2b03bc73daaa2e4353e8779366eaa33 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Sun, 21 Jan 2024 15:16:07 +1100 Subject: [PATCH 2/6] Remove DI experiments, and just leave stock standard playground. --- .../AzureCosmosDBDatabaseResource.cs | 2 +- .../AspireAzureCosmosDBExtensions.cs | 14 -------------- .../CosmosEndToEnd.ApiService/Program.cs | 12 ++++-------- 3 files changed, 5 insertions(+), 23 deletions(-) diff --git a/src/Aspire.Hosting.Azure/AzureCosmosDBDatabaseResource.cs b/src/Aspire.Hosting.Azure/AzureCosmosDBDatabaseResource.cs index 500d6c5a831..2eb92d0d829 100644 --- a/src/Aspire.Hosting.Azure/AzureCosmosDBDatabaseResource.cs +++ b/src/Aspire.Hosting.Azure/AzureCosmosDBDatabaseResource.cs @@ -26,5 +26,5 @@ public AzureCosmosDBDatabaseResource(string name, AzureCosmosDBResource parent) /// Gets the connection string to use for this database. /// /// The connection string to use for this database. - public string? GetConnectionString() => ConnectionString ?? $"{Parent.GetConnectionString()}Database={Name};"; // HACK: Will go away when we get rid of Azure Provisioner package. + public string? GetConnectionString() => ConnectionString ?? Parent.GetConnectionString(); // HACK: Will go away when we get rid of Azure Provisioner package. } diff --git a/src/Components/Aspire.Microsoft.Azure.Cosmos/AspireAzureCosmosDBExtensions.cs b/src/Components/Aspire.Microsoft.Azure.Cosmos/AspireAzureCosmosDBExtensions.cs index db784cd480e..75548a9dad5 100644 --- a/src/Components/Aspire.Microsoft.Azure.Cosmos/AspireAzureCosmosDBExtensions.cs +++ b/src/Components/Aspire.Microsoft.Azure.Cosmos/AspireAzureCosmosDBExtensions.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Data.Common; using Aspire.Hosting.Azure.Cosmos; using Aspire.Microsoft.Azure.Cosmos; using Azure.Identity; @@ -109,19 +108,6 @@ private static void AddAzureCosmosDB( if (serviceKey is null) { builder.Services.AddSingleton(_ => ConfigureDb()); - - var csBuilder = new DbConnectionStringBuilder(); - csBuilder.ConnectionString = settings.ConnectionString; - - if (csBuilder.TryGetValue("Database", out var databaseName)) - { - builder.Services.AddSingleton(sp => - { - var client = sp.GetRequiredService(); - var database = client.CreateDatabaseIfNotExistsAsync(databaseName.ToString()).Result.Database; - return database; - }); - } } else { diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Program.cs b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Program.cs index 52c71acf8fa..520ffefbde3 100644 --- a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Program.cs +++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Program.cs @@ -22,17 +22,13 @@ clientOptions.Serializer = new StjSerializer(new System.Text.Json.JsonSerializerOptions()); }); -builder.Services.AddKeyedSingleton("entries", (sp, _) => -{ - var db = sp.GetRequiredService(); - var container = db.CreateContainerIfNotExistsAsync("entries", "/sessionId").Result.Container; - return container; -}); - var app = builder.Build(); -app.MapGet("/", async ([FromKeyedServices("entries")]Container container) => +app.MapGet("/", async (CosmosClient cosmosClient) => { + var db = (await cosmosClient.CreateDatabaseIfNotExistsAsync("db").ConfigureAwait(false)).Database; + var container = (await db.CreateContainerIfNotExistsAsync("entries", "/sessionId").ConfigureAwait(false)).Container; + // Add an entry to the database on each request. await container.CreateItemAsync(new Entry(Guid.NewGuid().ToString(), sessionId)).ConfigureAwait(false); From fbba8cf3d65519ed8590d6b551f6187e1e8a5f65 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Sun, 21 Jan 2024 15:35:41 +1100 Subject: [PATCH 3/6] Remove STJ serializer. --- .../CosmosEndToEnd.ApiService/Program.cs | 31 +++++-------- .../StjCosmosSerializer.cs | 45 ------------------- 2 files changed, 12 insertions(+), 64 deletions(-) delete mode 100644 src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/StjCosmosSerializer.cs diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Program.cs b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Program.cs index 520ffefbde3..7de846974a4 100644 --- a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Program.cs +++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Program.cs @@ -1,26 +1,18 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Text.Json.Serialization; -using CosmosEndToEnd.ApiService; using Microsoft.Azure.Cosmos; +using Newtonsoft.Json; var sessionId = Guid.NewGuid().ToString(); var builder = WebApplication.CreateBuilder(args); builder.AddServiceDefaults(); -builder.AddAzureCosmosDB("db", - settings => - { - settings.IgnoreEmulatorCertificate = true; - }, - clientOptions => - { - // Default serializer for Cosmos V3 client is JSON.NET, this changes - // us to use S.T.J for this playground. - clientOptions.Serializer = new StjSerializer(new System.Text.Json.JsonSerializerOptions()); - }); +builder.AddAzureCosmosDB("db", settings => +{ + settings.IgnoreEmulatorCertificate = true; +}); var app = builder.Build(); @@ -30,7 +22,8 @@ var container = (await db.CreateContainerIfNotExistsAsync("entries", "/sessionId").ConfigureAwait(false)).Container; // Add an entry to the database on each request. - await container.CreateItemAsync(new Entry(Guid.NewGuid().ToString(), sessionId)).ConfigureAwait(false); + var newEntry = new Entry() { Id = Guid.NewGuid().ToString(), SessionId = Guid.NewGuid().ToString() }; + await container.CreateItemAsync(newEntry).ConfigureAwait(false); var entries = new List(); var iterator = container.GetItemQueryIterator(requestOptions: new QueryRequestOptions() { MaxItemCount = 5 }); @@ -56,11 +49,11 @@ app.Run(); -public class Entry(string id, string sessionId) +public class Entry { - [JsonPropertyName("id")] - public string Id { get; set; } = id; + [JsonProperty("id")] + public string? Id { get; set; } - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = sessionId; + [JsonProperty("sessionId")] + public string? SessionId { get; set; } } diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/StjCosmosSerializer.cs b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/StjCosmosSerializer.cs deleted file mode 100644 index a4e3b64c4a6..00000000000 --- a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/StjCosmosSerializer.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Text.Json; -using Azure.Core.Serialization; -using Microsoft.Azure.Cosmos; - -namespace CosmosEndToEnd.ApiService; - -public class StjSerializer: CosmosSerializer -{ - private readonly JsonObjectSerializer _systemTextJsonSerializer; - - public StjSerializer(JsonSerializerOptions jsonSerializerOptions) - { - this._systemTextJsonSerializer = new JsonObjectSerializer(jsonSerializerOptions); - } - - public override T FromStream(Stream stream) - { - using (stream) - { - if (stream.CanSeek - && stream.Length == 0) - { - return default!; - } - - if (typeof(Stream).IsAssignableFrom(typeof(T))) - { - return (T)(object)stream; - } - - return (T)this._systemTextJsonSerializer.Deserialize(stream, typeof(T), default)!; - } - } - - public override Stream ToStream(T input) - { - MemoryStream streamPayload = new MemoryStream(); - this._systemTextJsonSerializer.Serialize(streamPayload, input, input.GetType(), default); - streamPayload.Position = 0; - return streamPayload; - } -} From db955d73b9bdc74ea66ef27f47e7e1caa9449b29 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Sun, 21 Jan 2024 15:47:39 +1100 Subject: [PATCH 4/6] Suppres CA2007 for playground code. --- .editorconfig | 3 +++ .../CosmosEndToEnd/CosmosEndToEnd.ApiService/Program.cs | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.editorconfig b/.editorconfig index 1e311974190..7b89a017365 100644 --- a/.editorconfig +++ b/.editorconfig @@ -476,3 +476,6 @@ dotnet_diagnostic.IDE0005.severity = silent [{*.razor.cs,src/Aspire.Dashboard/Components/**.cs}] # CA2007: Consider calling ConfigureAwait on the awaited task dotnet_diagnostic.CA2007.severity = silent + +[{src/Playgrounds/**.cs}] +dotnet_diagnostic.CA2007.severity = silent diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Program.cs b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Program.cs index 7de846974a4..c65922e4e0d 100644 --- a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Program.cs +++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Program.cs @@ -18,8 +18,8 @@ app.MapGet("/", async (CosmosClient cosmosClient) => { - var db = (await cosmosClient.CreateDatabaseIfNotExistsAsync("db").ConfigureAwait(false)).Database; - var container = (await db.CreateContainerIfNotExistsAsync("entries", "/sessionId").ConfigureAwait(false)).Container; + var db = (await cosmosClient.CreateDatabaseIfNotExistsAsync("db")).Database; + var container = (await db.CreateContainerIfNotExistsAsync("entries", "/sessionId")).Container; // Add an entry to the database on each request. var newEntry = new Entry() { Id = Guid.NewGuid().ToString(), SessionId = Guid.NewGuid().ToString() }; From 56b82b7edd1a6ef99c8700b236483dc6bfd00efe Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Sun, 21 Jan 2024 16:20:04 +1100 Subject: [PATCH 5/6] Remove session ID and unneeded configreawaits. --- .../CosmosEndToEnd.ApiService/Program.cs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Program.cs b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Program.cs index c65922e4e0d..d678b1ece4d 100644 --- a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Program.cs +++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Program.cs @@ -4,8 +4,6 @@ using Microsoft.Azure.Cosmos; using Newtonsoft.Json; -var sessionId = Guid.NewGuid().ToString(); - var builder = WebApplication.CreateBuilder(args); builder.AddServiceDefaults(); @@ -19,11 +17,11 @@ app.MapGet("/", async (CosmosClient cosmosClient) => { var db = (await cosmosClient.CreateDatabaseIfNotExistsAsync("db")).Database; - var container = (await db.CreateContainerIfNotExistsAsync("entries", "/sessionId")).Container; + var container = (await db.CreateContainerIfNotExistsAsync("entries", "/Id")).Container; // Add an entry to the database on each request. - var newEntry = new Entry() { Id = Guid.NewGuid().ToString(), SessionId = Guid.NewGuid().ToString() }; - await container.CreateItemAsync(newEntry).ConfigureAwait(false); + var newEntry = new Entry() { Id = Guid.NewGuid().ToString() }; + await container.CreateItemAsync(newEntry); var entries = new List(); var iterator = container.GetItemQueryIterator(requestOptions: new QueryRequestOptions() { MaxItemCount = 5 }); @@ -32,7 +30,7 @@ while (iterator.HasMoreResults) { batchCount++; - var batch = await iterator.ReadNextAsync().ConfigureAwait(false); + var batch = await iterator.ReadNextAsync(); foreach (var entry in batch) { entries.Add(entry); @@ -53,7 +51,4 @@ public class Entry { [JsonProperty("id")] public string? Id { get; set; } - - [JsonProperty("sessionId")] - public string? SessionId { get; set; } } From 67986471620b71f4ca78525ac206a2fb9d90c8bb Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Sun, 21 Jan 2024 16:26:45 +1100 Subject: [PATCH 6/6] Making service defaults reusable across playground projects. --- Aspire.sln | 15 +++++++-------- .../CosmosEndToEnd.ApiService.csproj | 2 +- .../Extensions.cs | 0 .../Playground.ServiceDefaults.csproj} | 4 ++-- 4 files changed, 10 insertions(+), 11 deletions(-) rename src/Playgrounds/{CosmosEndToEnd/CosmosEndToEnd.ServiceDefaults => Playground.ServiceDefaults}/Extensions.cs (100%) rename src/Playgrounds/{CosmosEndToEnd/CosmosEndToEnd.ServiceDefaults/CosmosEndToEnd.ServiceDefaults.csproj => Playground.ServiceDefaults/Playground.ServiceDefaults.csproj} (77%) diff --git a/Aspire.sln b/Aspire.sln index 9ceffb97953..2ae0159929a 100644 --- a/Aspire.sln +++ b/Aspire.sln @@ -1,4 +1,3 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31903.59 @@ -200,9 +199,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CosmosEndToEnd", "CosmosEnd EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CosmosEndToEnd.AppHost", "src\Playgrounds\CosmosEndToEnd\CosmosEndToEnd.AppHost\CosmosEndToEnd.AppHost.csproj", "{51DDD6BC-1D6C-466A-B509-FC49E3BD72E4}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CosmosEndToEnd.ServiceDefaults", "src\Playgrounds\CosmosEndToEnd\CosmosEndToEnd.ServiceDefaults\CosmosEndToEnd.ServiceDefaults.csproj", "{71A215C3-61E3-4E46-880C-286C794AD7B3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CosmosEndToEnd.ApiService", "src\Playgrounds\CosmosEndToEnd\CosmosEndToEnd.ApiService\CosmosEndToEnd.ApiService.csproj", "{EABB20A8-CDA2-4AFE-A5B1-FB631200CD64}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CosmosEndToEnd.ApiService", "src\Playgrounds\CosmosEndToEnd\CosmosEndToEnd.ApiService\CosmosEndToEnd.ApiService.csproj", "{EABB20A8-CDA2-4AFE-A5B1-FB631200CD64}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Playground.ServiceDefaults", "src\Playgrounds\Playground.ServiceDefaults\Playground.ServiceDefaults.csproj", "{25208C6F-0A9D-4D60-9EDD-256C9891B1CD}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -534,14 +533,14 @@ Global {51DDD6BC-1D6C-466A-B509-FC49E3BD72E4}.Debug|Any CPU.Build.0 = Debug|Any CPU {51DDD6BC-1D6C-466A-B509-FC49E3BD72E4}.Release|Any CPU.ActiveCfg = Release|Any CPU {51DDD6BC-1D6C-466A-B509-FC49E3BD72E4}.Release|Any CPU.Build.0 = Release|Any CPU - {71A215C3-61E3-4E46-880C-286C794AD7B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {71A215C3-61E3-4E46-880C-286C794AD7B3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {71A215C3-61E3-4E46-880C-286C794AD7B3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {71A215C3-61E3-4E46-880C-286C794AD7B3}.Release|Any CPU.Build.0 = Release|Any CPU {EABB20A8-CDA2-4AFE-A5B1-FB631200CD64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EABB20A8-CDA2-4AFE-A5B1-FB631200CD64}.Debug|Any CPU.Build.0 = Debug|Any CPU {EABB20A8-CDA2-4AFE-A5B1-FB631200CD64}.Release|Any CPU.ActiveCfg = Release|Any CPU {EABB20A8-CDA2-4AFE-A5B1-FB631200CD64}.Release|Any CPU.Build.0 = Release|Any CPU + {25208C6F-0A9D-4D60-9EDD-256C9891B1CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {25208C6F-0A9D-4D60-9EDD-256C9891B1CD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {25208C6F-0A9D-4D60-9EDD-256C9891B1CD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {25208C6F-0A9D-4D60-9EDD-256C9891B1CD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -634,8 +633,8 @@ Global {A8CB331A-1247-41D9-8118-538E5A2CC9DF} = {4981B3A5-4AFD-4191-BF7D-8692D9783D60} {DBEDDF76-1C33-4943-8CCB-337A7D48AFF5} = {DF8ADBC2-5B15-422F-A703-C60711D5552D} {51DDD6BC-1D6C-466A-B509-FC49E3BD72E4} = {DBEDDF76-1C33-4943-8CCB-337A7D48AFF5} - {71A215C3-61E3-4E46-880C-286C794AD7B3} = {DBEDDF76-1C33-4943-8CCB-337A7D48AFF5} {EABB20A8-CDA2-4AFE-A5B1-FB631200CD64} = {DBEDDF76-1C33-4943-8CCB-337A7D48AFF5} + {25208C6F-0A9D-4D60-9EDD-256C9891B1CD} = {DF8ADBC2-5B15-422F-A703-C60711D5552D} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6DCEDFEC-988E-4CB3-B45B-191EB5086E0C} diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/CosmosEndToEnd.ApiService.csproj b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/CosmosEndToEnd.ApiService.csproj index 077dd67d8b2..211dd11a47b 100644 --- a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/CosmosEndToEnd.ApiService.csproj +++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/CosmosEndToEnd.ApiService.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ServiceDefaults/Extensions.cs b/src/Playgrounds/Playground.ServiceDefaults/Extensions.cs similarity index 100% rename from src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ServiceDefaults/Extensions.cs rename to src/Playgrounds/Playground.ServiceDefaults/Extensions.cs diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ServiceDefaults/CosmosEndToEnd.ServiceDefaults.csproj b/src/Playgrounds/Playground.ServiceDefaults/Playground.ServiceDefaults.csproj similarity index 77% rename from src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ServiceDefaults/CosmosEndToEnd.ServiceDefaults.csproj rename to src/Playgrounds/Playground.ServiceDefaults/Playground.ServiceDefaults.csproj index 07fade10e2f..dde733791d9 100644 --- a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ServiceDefaults/CosmosEndToEnd.ServiceDefaults.csproj +++ b/src/Playgrounds/Playground.ServiceDefaults/Playground.ServiceDefaults.csproj @@ -19,8 +19,8 @@ - - + +