Skip to content

Commit

Permalink
Wait for dependent resources before depolying the .dacpac (#380)
Browse files Browse the repository at this point in the history
* Wait for dependent resources before depolying the .dacpac

fixes #373

* fix up

* move call

* Add test to verify wait for dependent resource

---------

Co-authored-by: Alireza Baloochi <[email protected]>
  • Loading branch information
ErikEJ and Alirexaa authored Jan 17, 2025
1 parent 66046c2 commit a12e081
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
var builder = DistributedApplication.CreateBuilder(args);

var server = builder.AddSqlServer("sql")
.AddDatabase("TargetDatabase");
var server = builder.AddSqlServer("sql");

builder.AddSqlProject<Projects.SdkProject>("sdk-project")
.WithReference(server);
var database = server.AddDatabase("TargetDatabase");

var otherDatabase = server.AddDatabase("OtherTargetDatabase");

var sdkProject = builder.AddSqlProject<Projects.SdkProject>("sdk-project")
.WithReference(database);

var otherProject = builder.AddSqlProject<Projects.SdkProject>("other-sdk-project")
.WithReference(otherDatabase)
.WaitForCompletion(sdkProject);

builder.AddSqlPackage<Packages.ErikEJ_Dacpac_Chinook>("chinook")
.WithReference(server);
.WithReference(database);

builder.Build().Run();
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,10 @@ internal static IResourceBuilder<TResource> InternalWithReference<TResource>(thi
builder.ApplicationBuilder.Services.TryAddSingleton<IDacpacDeployer, DacpacDeployer>();
builder.ApplicationBuilder.Services.TryAddSingleton<SqlProjectPublishService>();

builder.ApplicationBuilder.Eventing.Subscribe<ResourceReadyEvent>(target.Resource, (resourceReady, ct) =>
builder.ApplicationBuilder.Eventing.Subscribe<ResourceReadyEvent>(target.Resource, async (resourceReady, ct) =>
{
var service = resourceReady.Services.GetRequiredService<SqlProjectPublishService>();
return service.PublishSqlProject(builder.Resource, target.Resource, ct);
await service.PublishSqlProject(builder.Resource, target.Resource, ct);
});

builder.WaitFor(target);
Expand All @@ -184,7 +184,7 @@ internal static IResourceBuilder<TResource> InternalWithReference<TResource>(thi
await service.PublishSqlProject(builder.Resource, target.Resource, context.CancellationToken);
return new ExecuteCommandResult { Success = true };
}, updateState: (context) => context.ResourceSnapshot?.State?.Text == KnownResourceStates.Finished ? ResourceCommandState.Enabled : ResourceCommandState.Disabled,
displayDescription: "Redeploys the SQL Server Database to the target database.",
displayDescription: "Redeploys the SQL Server Database Project to the target database.",
iconName: "ArrowReset",
iconVariant: IconVariant.Filled,
isHighlighted: true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public async Task PublishSqlProject(IResourceWithDacpac resource, SqlServerDatab

try
{
await resourceNotificationService.WaitForDependenciesAsync(resource, cancellationToken);

var dacpacPath = resource.GetDacpacPath();
if (!Path.IsPathRooted(dacpacPath))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ namespace CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests;
public class AppHostTests(AspireIntegrationTestFixture<Projects.CommunityToolkit_Aspire_Hosting_SqlDatabaseProjects_AppHost> fixture) : IClassFixture<AspireIntegrationTestFixture<Projects.CommunityToolkit_Aspire_Hosting_SqlDatabaseProjects_AppHost>>
{
[Theory]
[InlineData("sdk-project", "SdkProject")]
[InlineData("chinook", "InvoiceLine")]
public async Task ProjectBasedResourceStartsAndRespondsOk(string resourceName, string tableName)
[InlineData("sdk-project", "SdkProject", "TargetDatabase")]
[InlineData("other-sdk-project", "SdkProject", "OtherTargetDatabase")]
[InlineData("chinook", "InvoiceLine", "TargetDatabase")]
public async Task ProjectBasedResourceStartsAndRespondsOk(string resourceName, string tableName, string database)
{
await fixture.ResourceNotificationService.WaitForResourceAsync(resourceName, KnownResourceStates.Finished).WaitAsync(TimeSpan.FromMinutes(5));

string? connectionString = await fixture.GetConnectionString("TargetDatabase");
string? connectionString = await fixture.GetConnectionString(database);
Assert.NotNull(connectionString);

using var connection = new SqlConnection(connectionString);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using Aspire.Components.Common.Tests;
using Aspire.Hosting;
using Aspire.Hosting.Utils;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Projects;
using Xunit.Abstractions;

namespace CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests;

[RequiresDocker]
public class FunctionalTests(ITestOutputHelper testOutputHelper)
{
[Fact]
public async Task VerifyPublishSqlProjectWaitForDependentResources()
{
var cts = new CancellationTokenSource(TimeSpan.FromMinutes(10));
using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper);

var healthCheckTcs = new TaskCompletionSource<HealthCheckResult>();
builder.Services.AddHealthChecks().AddAsyncCheck("blocking_check", () =>
{
return healthCheckTcs.Task;
});

var resource = builder.AddSqlServer("resource")
.WithHealthCheck("blocking_check");

var database = resource.AddDatabase("TargetDatabase");

var otherDatabase = resource.AddDatabase("OtherTargetDatabase");

var dependentResource = builder.AddSqlProject<Projects.SdkProject>("dependentresource")
.WithReference(database);

var otherDependentResource = builder.AddSqlProject<Projects.SdkProject>("other-sdk-project")
.WithReference(otherDatabase)
.WaitForCompletion(dependentResource);

using var app = builder.Build();

var pendingStart = app.StartAsync(cts.Token);

var rns = app.Services.GetRequiredService<ResourceNotificationService>();

await rns.WaitForResourceAsync(resource.Resource.Name, KnownResourceStates.Running, cts.Token);

await rns.WaitForResourceAsync(dependentResource.Resource.Name, "Pending", cts.Token);

await rns.WaitForResourceAsync(otherDependentResource.Resource.Name, "Pending", cts.Token);


healthCheckTcs.SetResult(HealthCheckResult.Healthy());

await rns.WaitForResourceAsync(resource.Resource.Name, re => re.Snapshot.HealthStatus == HealthStatus.Healthy, cts.Token);

await rns.WaitForResourceAsync(dependentResource.Resource.Name, "Publishing", cts.Token);

await rns.WaitForResourceAsync(dependentResource.Resource.Name, KnownResourceStates.Finished, cts.Token);

await rns.WaitForResourceAsync(otherDependentResource.Resource.Name, "Publishing", cts.Token);

await rns.WaitForResourceAsync(otherDependentResource.Resource.Name, KnownResourceStates.Finished, cts.Token);

await pendingStart;

await app.StopAsync();
}
}

0 comments on commit a12e081

Please sign in to comment.