Skip to content

Commit 4b85f16

Browse files
Updates for shared SQL (#11)
1 parent 271e4f7 commit 4b85f16

File tree

16 files changed

+347
-8
lines changed

16 files changed

+347
-8
lines changed

Microsoft.Health.sln

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Health.SqlServer.
3737
EndProject
3838
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Health.SqlServer.UnitTests", "src\Microsoft.Health.SqlServer.UnitTests\Microsoft.Health.SqlServer.UnitTests.csproj", "{8FEF74DC-94A5-4E20-9BE2-095941935EC4}"
3939
EndProject
40+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Health.SqlServer.Tests.E2E", "test\Microsoft.Health.SqlServer.Tests.E2E\Microsoft.Health.SqlServer.Tests.E2E.csproj", "{C74D5E00-8BE4-4C99-8A59-9D58D255C140}"
41+
EndProject
42+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{CCD9FF99-E177-446E-B9E5-9F570FD96A34}"
43+
EndProject
44+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Health.SqlServer.Web", "test\Microsoft.Health.SqlServer.Web\Microsoft.Health.SqlServer.Web.csproj", "{85781F6A-28D8-4850-A991-51157E5DF46E}"
45+
EndProject
4046
Global
4147
GlobalSection(SolutionConfigurationPlatforms) = preSolution
4248
Debug|Any CPU = Debug|Any CPU
@@ -91,6 +97,14 @@ Global
9197
{8FEF74DC-94A5-4E20-9BE2-095941935EC4}.Debug|Any CPU.Build.0 = Debug|Any CPU
9298
{8FEF74DC-94A5-4E20-9BE2-095941935EC4}.Release|Any CPU.ActiveCfg = Release|Any CPU
9399
{8FEF74DC-94A5-4E20-9BE2-095941935EC4}.Release|Any CPU.Build.0 = Release|Any CPU
100+
{C74D5E00-8BE4-4C99-8A59-9D58D255C140}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
101+
{C74D5E00-8BE4-4C99-8A59-9D58D255C140}.Debug|Any CPU.Build.0 = Debug|Any CPU
102+
{C74D5E00-8BE4-4C99-8A59-9D58D255C140}.Release|Any CPU.ActiveCfg = Release|Any CPU
103+
{C74D5E00-8BE4-4C99-8A59-9D58D255C140}.Release|Any CPU.Build.0 = Release|Any CPU
104+
{85781F6A-28D8-4850-A991-51157E5DF46E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
105+
{85781F6A-28D8-4850-A991-51157E5DF46E}.Debug|Any CPU.Build.0 = Debug|Any CPU
106+
{85781F6A-28D8-4850-A991-51157E5DF46E}.Release|Any CPU.ActiveCfg = Release|Any CPU
107+
{85781F6A-28D8-4850-A991-51157E5DF46E}.Release|Any CPU.Build.0 = Release|Any CPU
94108
EndGlobalSection
95109
GlobalSection(SolutionProperties) = preSolution
96110
HideSolutionNode = FALSE
@@ -108,6 +122,8 @@ Global
108122
{561B1075-FA22-4D4E-881B-366230423B2D} = {8AD2A324-DAB5-4380-94A5-31F7D817C384}
109123
{7650CF08-00B4-419B-8AF9-26E4079F08F2} = {8AD2A324-DAB5-4380-94A5-31F7D817C384}
110124
{8FEF74DC-94A5-4E20-9BE2-095941935EC4} = {8AD2A324-DAB5-4380-94A5-31F7D817C384}
125+
{C74D5E00-8BE4-4C99-8A59-9D58D255C140} = {CCD9FF99-E177-446E-B9E5-9F570FD96A34}
126+
{85781F6A-28D8-4850-A991-51157E5DF46E} = {CCD9FF99-E177-446E-B9E5-9F570FD96A34}
111127
EndGlobalSection
112128
GlobalSection(ExtensibilityGlobals) = postSolution
113129
RESX_SortFileContentOnSave = True

src/Microsoft.Health.SqlServer.Api.UnitTests/Features/Filters/HttpExceptionFilterTests.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
using Microsoft.AspNetCore.Mvc;
1212
using Microsoft.AspNetCore.Mvc.Abstractions;
1313
using Microsoft.AspNetCore.Mvc.Filters;
14-
using Microsoft.AspNetCore.Mvc.Infrastructure;
1514
using Microsoft.AspNetCore.Mvc.Routing;
1615
using Microsoft.AspNetCore.Routing;
1716
using Microsoft.Extensions.Logging.Abstractions;

src/Microsoft.Health.SqlServer/Features/Health/SqlServerHealthCheck.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@ public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context
3636
try
3737
{
3838
using (var connection = new SqlConnection(_configuration.ConnectionString))
39+
using (SqlCommand command = connection.CreateCommand())
3940
{
4041
await connection.OpenAsync(cancellationToken);
4142

42-
SqlCommand command = connection.CreateCommand();
4343
command.CommandText = "select @@DBTS";
4444

4545
await command.ExecuteScalarAsync(cancellationToken);

src/Microsoft.Health.SqlServer/Features/Schema/SchemaUpgradeRunner.cs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,9 @@ private void CompleteSchemaVersion(int schemaVersion)
6565
private void UpsertSchemaVersion(int schemaVersion, string status)
6666
{
6767
using (var connection = new SqlConnection(_sqlServerDataStoreConfiguration.ConnectionString))
68+
using (var upsertCommand = new SqlCommand("dbo.UpsertSchemaVersion", connection))
6869
{
69-
var upsertCommand = new SqlCommand("dbo.UpsertSchemaVersion", connection)
70-
{
71-
CommandType = CommandType.StoredProcedure,
72-
};
73-
70+
upsertCommand.CommandType = CommandType.StoredProcedure;
7471
upsertCommand.Parameters.AddWithValue("@version", schemaVersion);
7572
upsertCommand.Parameters.AddWithValue("@status", status);
7673

src/Microsoft.Health.SqlServer/Features/Storage/SqlErrorCodes.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ public static class SqlErrorCodes
2525
/// </summary>
2626
public const int MethodNotAllowed = CustomErrorCodeBase + 405;
2727

28+
/// <summary>
29+
/// The resource already exists
30+
/// </summary>
31+
public const int Conflict = CustomErrorCodeBase + 409;
32+
2833
/// <summary>
2934
/// An optimistic concurrency precondition failed
3035
/// </summary>

src/Microsoft.Health.SqlServer/Features/Storage/SqlServerModelInitializer.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,17 @@ public SqlServerModelInitializer(SqlServerDataStoreConfiguration configuration,
3232
SchemaInformation = schemaInformation;
3333
_logger = logger;
3434

35-
_initializationOperation = new RetryableInitializationOperation(Initialize);
35+
_initializationOperation = new RetryableInitializationOperation(() =>
36+
{
37+
if (!SchemaInformation.Current.HasValue)
38+
{
39+
_logger.LogError($"The current version of the database is not available. Unable in initialize {nameof(SqlServerModelInitializer)}.");
40+
throw new ServiceUnavailableException();
41+
}
42+
43+
return Initialize();
44+
});
45+
3646
if (SchemaInformation.Current != null)
3747
{
3848
// kick off initialization so that it can be ready for requests. Errors will be observed by requests when they call the method.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netcoreapp3.1</TargetFramework>
5+
<IsPackable>false</IsPackable>
6+
<RootNamespace>Microsoft.Health.SqlServer.Tests.E2E</RootNamespace>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
11+
<PackageReference Include="xunit" Version="2.4.0" />
12+
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
13+
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.0" />
14+
</ItemGroup>
15+
16+
<ItemGroup>
17+
<ProjectReference Include="..\..\src\Microsoft.Health.SqlServer.Api\Microsoft.Health.SqlServer.Api.csproj" />
18+
<ProjectReference Include="..\..\src\Microsoft.Health.SqlServer\Microsoft.Health.SqlServer.csproj" />
19+
<ProjectReference Include="..\Microsoft.Health.SqlServer.Web\Microsoft.Health.SqlServer.Web.csproj" />
20+
</ItemGroup>
21+
22+
</Project>
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
// -------------------------------------------------------------------------------------------------
2+
// Copyright (c) Microsoft Corporation. All rights reserved.
3+
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
4+
// -------------------------------------------------------------------------------------------------
5+
6+
using System;
7+
using System.Collections.Generic;
8+
using System.Net;
9+
using System.Net.Http;
10+
using System.Text;
11+
using System.Threading.Tasks;
12+
using Microsoft.AspNetCore.Mvc.Testing;
13+
using Microsoft.Health.SqlServer.Web;
14+
using Newtonsoft.Json.Linq;
15+
using Xunit;
16+
17+
namespace Microsoft.Health.SqlServer.Tests.E2E
18+
{
19+
public class SchemaTests : IClassFixture<WebApplicationFactory<Startup>>
20+
{
21+
private readonly WebApplicationFactory<Startup> _factory;
22+
private readonly HttpClient _client;
23+
24+
public SchemaTests(WebApplicationFactory<Startup> factory)
25+
{
26+
_factory = factory;
27+
_client = factory.CreateClient();
28+
}
29+
30+
public static IEnumerable<object[]> Data =>
31+
new List<object[]>
32+
{
33+
new object[] { "_schema/compatibility" },
34+
new object[] { "_schema/versions/current" },
35+
};
36+
37+
[Fact]
38+
public async Task GivenAServerThatHasSchemas_WhenRequestingAvailable_JsonShouldBeReturned()
39+
{
40+
var request = new HttpRequestMessage
41+
{
42+
Method = HttpMethod.Get,
43+
RequestUri = new Uri(_client.BaseAddress, "_schema/versions"),
44+
};
45+
46+
HttpResponseMessage response = await _client.SendAsync(request);
47+
48+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
49+
50+
var jArrayResponse = JArray.Parse(await response.Content.ReadAsStringAsync());
51+
52+
Assert.NotEmpty(jArrayResponse);
53+
54+
JToken firstResult = jArrayResponse.First;
55+
string scriptUrl = $"/_schema/versions/{firstResult["id"]}/script";
56+
Assert.Equal(scriptUrl, firstResult["script"]);
57+
}
58+
59+
[Theory]
60+
[MemberData(nameof(Data))]
61+
public async Task GivenGetMethod_WhenRequestingSchema_TheServerShouldReturnNotImplemented(string path)
62+
{
63+
await SendAndVerifyStatusCode(HttpMethod.Get, path, HttpStatusCode.NotImplemented);
64+
}
65+
66+
[Theory]
67+
[MemberData(nameof(Data))]
68+
public async Task GivenPostMethod_WhenRequestingSchema_TheServerShouldReturnNotFound(string path)
69+
{
70+
await SendAndVerifyStatusCode(HttpMethod.Post, path, HttpStatusCode.NotFound);
71+
}
72+
73+
[Theory]
74+
[MemberData(nameof(Data))]
75+
public async Task GivenPutMethod_WhenRequestingSchema_TheServerShouldReturnNotFound(string path)
76+
{
77+
await SendAndVerifyStatusCode(HttpMethod.Put, path, HttpStatusCode.NotFound);
78+
}
79+
80+
[Theory]
81+
[MemberData(nameof(Data))]
82+
public async Task GivenDeleteMethod_WhenRequestingSchema_TheServerShouldReturnNotFound(string path)
83+
{
84+
await SendAndVerifyStatusCode(HttpMethod.Delete, path, HttpStatusCode.NotFound);
85+
}
86+
87+
[Fact]
88+
public async Task GivenNonIntegerVersion_WhenRequestingScript_TheServerShouldReturnNotFound()
89+
{
90+
await SendAndVerifyStatusCode(HttpMethod.Get, "_schema/versions/abc/script", HttpStatusCode.NotFound);
91+
}
92+
93+
[Fact]
94+
public async Task GivenPostMethod_WhenRequestingScript_TheServerShouldReturnNotFound()
95+
{
96+
await SendAndVerifyStatusCode(HttpMethod.Post, "_schema/versions/1/script", HttpStatusCode.NotFound);
97+
}
98+
99+
[Fact]
100+
public async Task GivenPutMethod_WhenRequestingScript_TheServerShouldReturnNotFound()
101+
{
102+
await SendAndVerifyStatusCode(HttpMethod.Put, "_schema/versions/1/script", HttpStatusCode.NotFound);
103+
}
104+
105+
[Fact]
106+
public async Task GivenDeleteMethod_WhenRequestingScript_TheServerShouldReturnNotFound()
107+
{
108+
await SendAndVerifyStatusCode(HttpMethod.Delete, "_schema/versions/1/script", HttpStatusCode.NotFound);
109+
}
110+
111+
[Fact]
112+
public async Task GivenSchemaIdFound_WhenRequestingScript_TheServerShouldReturnScript()
113+
{
114+
var request = new HttpRequestMessage
115+
{
116+
Method = HttpMethod.Get,
117+
RequestUri = new Uri(_client.BaseAddress, "_schema/versions/1/script"),
118+
};
119+
HttpResponseMessage response = await _client.SendAsync(request);
120+
121+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
122+
123+
string script = response.Content.ToString();
124+
125+
Assert.NotEmpty(script);
126+
}
127+
128+
[Fact]
129+
public async Task GivenSchemaIdNotFound_WhenRequestingScript_TheServerShouldReturnNotFoundException()
130+
{
131+
await SendAndVerifyStatusCode(HttpMethod.Get, "_schema/versions/0/script", HttpStatusCode.NotFound);
132+
}
133+
134+
private async Task SendAndVerifyStatusCode(HttpMethod httpMethod, string path, HttpStatusCode httpStatusCode)
135+
{
136+
var request = new HttpRequestMessage
137+
{
138+
Method = httpMethod,
139+
RequestUri = new Uri(_client.BaseAddress, path),
140+
};
141+
142+
// Setting the contentType explicitly because POST/PUT/PATCH throws UnsupportedMediaType
143+
using (var content = new StringContent(" ", Encoding.UTF8, "application/json"))
144+
{
145+
request.Content = content;
146+
HttpResponseMessage response = await _client.SendAsync(request);
147+
Assert.Equal(httpStatusCode, response.StatusCode);
148+
}
149+
}
150+
}
151+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
SELECT 1
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// -------------------------------------------------------------------------------------------------
2+
// Copyright (c) Microsoft Corporation. All rights reserved.
3+
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
4+
// -------------------------------------------------------------------------------------------------
5+
6+
namespace Microsoft.Health.SqlServer.Web.Features.Schema
7+
{
8+
public enum SchemaVersion
9+
{
10+
Version1 = 1,
11+
Version2 = 2,
12+
}
13+
}

0 commit comments

Comments
 (0)