mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-10-15 01:54:43 +00:00
Modify brewery location dto to include denormalized data
This commit is contained in:
@@ -27,6 +27,8 @@ COPY ["Features/Features.Breweries/Features.Breweries.csproj", "Features/Feature
|
|||||||
COPY ["Features/Features.Breweries.Tests/Features.Breweries.Tests.csproj", "Features/Features.Breweries.Tests/"]
|
COPY ["Features/Features.Breweries.Tests/Features.Breweries.Tests.csproj", "Features/Features.Breweries.Tests/"]
|
||||||
COPY ["Features/Features.Emails/Features.Emails.csproj", "Features/Features.Emails/"]
|
COPY ["Features/Features.Emails/Features.Emails.csproj", "Features/Features.Emails/"]
|
||||||
COPY ["Features/Features.Emails.Tests/Features.Emails.Tests.csproj", "Features/Features.Emails.Tests/"]
|
COPY ["Features/Features.Emails.Tests/Features.Emails.Tests.csproj", "Features/Features.Emails.Tests/"]
|
||||||
|
COPY ["Features/Features.Locations/Features.Locations.csproj", "Features/Features.Locations/"]
|
||||||
|
COPY ["Features/Features.Locations.Tests/Features.Locations.Tests.csproj", "Features/Features.Locations.Tests/"]
|
||||||
COPY ["Features/Features.UserManagement/Features.UserManagement.csproj", "Features/Features.UserManagement/"]
|
COPY ["Features/Features.UserManagement/Features.UserManagement.csproj", "Features/Features.UserManagement/"]
|
||||||
COPY ["Features/Features.UserManagement.Tests/Features.UserManagement.Tests.csproj", "Features/Features.UserManagement.Tests/"]
|
COPY ["Features/Features.UserManagement.Tests/Features.UserManagement.Tests.csproj", "Features/Features.UserManagement.Tests/"]
|
||||||
COPY ["Infrastructure/Infrastructure.Email/Infrastructure.Email.csproj", "Infrastructure/Infrastructure.Email/"]
|
COPY ["Infrastructure/Infrastructure.Email/Infrastructure.Email.csproj", "Infrastructure/Infrastructure.Email/"]
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ COPY ["Features/Features.Breweries/Features.Breweries.csproj", "Features/Feature
|
|||||||
COPY ["Features/Features.Breweries.Tests/Features.Breweries.Tests.csproj", "Features/Features.Breweries.Tests/"]
|
COPY ["Features/Features.Breweries.Tests/Features.Breweries.Tests.csproj", "Features/Features.Breweries.Tests/"]
|
||||||
COPY ["Features/Features.Emails/Features.Emails.csproj", "Features/Features.Emails/"]
|
COPY ["Features/Features.Emails/Features.Emails.csproj", "Features/Features.Emails/"]
|
||||||
COPY ["Features/Features.Emails.Tests/Features.Emails.Tests.csproj", "Features/Features.Emails.Tests/"]
|
COPY ["Features/Features.Emails.Tests/Features.Emails.Tests.csproj", "Features/Features.Emails.Tests/"]
|
||||||
|
COPY ["Features/Features.Locations/Features.Locations.csproj", "Features/Features.Locations/"]
|
||||||
|
COPY ["Features/Features.Locations.Tests/Features.Locations.Tests.csproj", "Features/Features.Locations.Tests/"]
|
||||||
COPY ["Features/Features.UserManagement/Features.UserManagement.csproj", "Features/Features.UserManagement/"]
|
COPY ["Features/Features.UserManagement/Features.UserManagement.csproj", "Features/Features.UserManagement/"]
|
||||||
COPY ["Features/Features.UserManagement.Tests/Features.UserManagement.Tests.csproj", "Features/Features.UserManagement.Tests/"]
|
COPY ["Features/Features.UserManagement.Tests/Features.UserManagement.Tests.csproj", "Features/Features.UserManagement.Tests/"]
|
||||||
COPY ["Infrastructure/Infrastructure.Email/Infrastructure.Email.csproj", "Infrastructure/Infrastructure.Email/"]
|
COPY ["Infrastructure/Infrastructure.Email/Infrastructure.Email.csproj", "Infrastructure/Infrastructure.Email/"]
|
||||||
|
|||||||
@@ -2,14 +2,15 @@
|
|||||||
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
|
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
|
||||||
ARG BUILD_CONFIGURATION=Release
|
ARG BUILD_CONFIGURATION=Release
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
# Copy everything from the context (src/Core/Database)
|
# Copy everything from the context (src/Core), matching Database.Seed/Dockerfile's context so
|
||||||
|
# ProjectReference paths outside Database/ (e.g. Infrastructure.Sql) resolve.
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN dotnet restore "./Database.Migrations/Database.Migrations.csproj"
|
RUN dotnet restore "./Database/Database.Migrations/Database.Migrations.csproj"
|
||||||
RUN dotnet build "./Database.Migrations/Database.Migrations.csproj" -c $BUILD_CONFIGURATION -o /app/build
|
RUN dotnet build "./Database/Database.Migrations/Database.Migrations.csproj" -c $BUILD_CONFIGURATION -o /app/build
|
||||||
|
|
||||||
FROM build AS publish
|
FROM build AS publish
|
||||||
ARG BUILD_CONFIGURATION=Release
|
ARG BUILD_CONFIGURATION=Release
|
||||||
RUN dotnet publish "./Database.Migrations/Database.Migrations.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
|
RUN dotnet publish "./Database/Database.Migrations/Database.Migrations.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
|
||||||
|
|
||||||
FROM mcr.microsoft.com/dotnet/runtime:10.0 AS final
|
FROM mcr.microsoft.com/dotnet/runtime:10.0 AS final
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|||||||
@@ -324,7 +324,7 @@ CREATE TABLE BreweryPost -- A user cannot be deleted if they have a post
|
|||||||
|
|
||||||
PostedByID UNIQUEIDENTIFIER NOT NULL,
|
PostedByID UNIQUEIDENTIFIER NOT NULL,
|
||||||
|
|
||||||
Description NVARCHAR(512) NOT NULL,
|
Description NVARCHAR(MAX) NOT NULL,
|
||||||
|
|
||||||
CreatedAt DATETIME NOT NULL
|
CreatedAt DATETIME NOT NULL
|
||||||
CONSTRAINT DF_BreweryPost_CreatedAt DEFAULT GETDATE(),
|
CONSTRAINT DF_BreweryPost_CreatedAt DEFAULT GETDATE(),
|
||||||
|
|||||||
@@ -186,11 +186,13 @@ static async Task LoadBreweriesIntoDatabaseAsync(
|
|||||||
await breweryRepository.CreateAsync(
|
await breweryRepository.CreateAsync(
|
||||||
new BreweryPost
|
new BreweryPost
|
||||||
{
|
{
|
||||||
|
BreweryPostId = Guid.NewGuid(),
|
||||||
BreweryName = breweryRecord.Brewery.NameEn,
|
BreweryName = breweryRecord.Brewery.NameEn,
|
||||||
Description = breweryRecord.Brewery.DescriptionEn,
|
Description = breweryRecord.Brewery.DescriptionEn,
|
||||||
PostedById = posterUserIds[i % posterUserIds.Count],
|
PostedById = posterUserIds[i % posterUserIds.Count],
|
||||||
Location = new BreweryPostLocation
|
Location = new BreweryPostLocation
|
||||||
{
|
{
|
||||||
|
BreweryPostLocationId = Guid.NewGuid(),
|
||||||
CityId = cityId,
|
CityId = cityId,
|
||||||
AddressLine1 = "Address unavailable",
|
AddressLine1 = "Address unavailable",
|
||||||
PostalCode = breweryRecord.Address.PostalCode,
|
PostalCode = breweryRecord.Address.PostalCode,
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
namespace Database.Seed;
|
|
||||||
|
|
||||||
public interface IExportDatabase
|
|
||||||
{
|
|
||||||
void SeedLocations(IEnumerable<Location> locations);
|
|
||||||
void SeedUsers(IEnumerable<User> users);
|
|
||||||
|
|
||||||
void SeedBreweries(IEnumerable<Brewery> breweries);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IImportDatabase
|
|
||||||
{
|
|
||||||
IEnumerable<Location> GetLocations();
|
|
||||||
IEnumerable<User> GetUsers();
|
|
||||||
|
|
||||||
IEnumerable<Brewery> GetBreweries();
|
|
||||||
}
|
|
||||||
@@ -8,6 +8,7 @@ public class BreweryPostLocation
|
|||||||
public string? AddressLine2 { get; set; }
|
public string? AddressLine2 { get; set; }
|
||||||
public string PostalCode { get; set; } = string.Empty;
|
public string PostalCode { get; set; } = string.Empty;
|
||||||
public Guid CityId { get; set; }
|
public Guid CityId { get; set; }
|
||||||
|
public City? City { get; set; }
|
||||||
public byte[]? Coordinates { get; set; }
|
public byte[]? Coordinates { get; set; }
|
||||||
public byte[]? Timer { get; set; }
|
public byte[]? Timer { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,4 +5,34 @@ public class City
|
|||||||
public Guid CityId { get; set; }
|
public Guid CityId { get; set; }
|
||||||
public string CityName { get; set; } = string.Empty;
|
public string CityName { get; set; } = string.Empty;
|
||||||
public Guid StateProvinceId { get; set; }
|
public Guid StateProvinceId { get; set; }
|
||||||
|
|
||||||
|
public StateProvince? StateProvince { get; set; }
|
||||||
|
|
||||||
|
public byte[]? Timer { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class StateProvince
|
||||||
|
{
|
||||||
|
public Guid StateProvinceId { get; set; }
|
||||||
|
public string StateProvinceName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
// this is the iso 3166-2 code for the state/province, e.g. "CA" for California, "ON" for Ontario, etc.
|
||||||
|
// and is rendered like US-CA for California, CA-ON for Ontario, etc.
|
||||||
|
public string ISO3166_2 { get; set; } = string.Empty;
|
||||||
|
public Guid CountryId { get; set; }
|
||||||
|
|
||||||
|
public Country? Country { get; set; }
|
||||||
|
|
||||||
|
public byte[]? Timer { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Country
|
||||||
|
{
|
||||||
|
public Guid CountryId { get; set; }
|
||||||
|
public string CountryName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string ISO3166_1 { get; set; } = string.Empty; // this is the iso 3166-1 alpha-2 code for the country, e.g. "US" for United States, "CA" for Canada, etc.
|
||||||
|
|
||||||
|
public byte[]? Timer { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,8 +20,11 @@ public class BreweryRepositoryTests
|
|||||||
MockDbConnection conn = new();
|
MockDbConnection conn = new();
|
||||||
|
|
||||||
Guid locationId = Guid.NewGuid();
|
Guid locationId = Guid.NewGuid();
|
||||||
|
Guid cityId = Guid.NewGuid();
|
||||||
|
Guid stateProvinceId = Guid.NewGuid();
|
||||||
|
Guid countryId = Guid.NewGuid();
|
||||||
|
|
||||||
conn.Mocks.When(cmd => cmd.CommandText.Contains("INNER JOIN dbo.BreweryPostLocation"))
|
conn.Mocks.When(cmd => cmd.CommandText.Contains("LEFT JOIN dbo.BreweryPostLocation"))
|
||||||
.ReturnsTable(
|
.ReturnsTable(
|
||||||
MockTable
|
MockTable
|
||||||
.WithColumns(
|
.WithColumns(
|
||||||
@@ -37,7 +40,14 @@ public class BreweryRepositoryTests
|
|||||||
("AddressLine1", typeof(string)),
|
("AddressLine1", typeof(string)),
|
||||||
("AddressLine2", typeof(string)),
|
("AddressLine2", typeof(string)),
|
||||||
("PostalCode", typeof(string)),
|
("PostalCode", typeof(string)),
|
||||||
("Coordinates", typeof(byte[]))
|
("Coordinates", typeof(byte[])),
|
||||||
|
("CityName", typeof(string)),
|
||||||
|
("StateProvinceId", typeof(Guid)),
|
||||||
|
("StateProvinceName", typeof(string)),
|
||||||
|
("ISO3166_2", typeof(string)),
|
||||||
|
("CountryId", typeof(Guid)),
|
||||||
|
("CountryName", typeof(string)),
|
||||||
|
("ISO3166_1", typeof(string))
|
||||||
)
|
)
|
||||||
.AddRow(
|
.AddRow(
|
||||||
breweryId,
|
breweryId,
|
||||||
@@ -48,11 +58,18 @@ public class BreweryRepositoryTests
|
|||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
locationId,
|
locationId,
|
||||||
Guid.NewGuid(),
|
cityId,
|
||||||
"123 Main St",
|
"123 Main St",
|
||||||
null,
|
null,
|
||||||
"12345",
|
"12345",
|
||||||
null
|
null,
|
||||||
|
"Portland",
|
||||||
|
stateProvinceId,
|
||||||
|
"Oregon",
|
||||||
|
"US-OR",
|
||||||
|
countryId,
|
||||||
|
"United States",
|
||||||
|
"US"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -62,13 +79,19 @@ public class BreweryRepositoryTests
|
|||||||
result!.BreweryPostId.Should().Be(breweryId);
|
result!.BreweryPostId.Should().Be(breweryId);
|
||||||
result.Location.Should().NotBeNull();
|
result.Location.Should().NotBeNull();
|
||||||
result.Location!.BreweryPostLocationId.Should().Be(locationId);
|
result.Location!.BreweryPostLocationId.Should().Be(locationId);
|
||||||
|
result.Location.City.Should().NotBeNull();
|
||||||
|
result.Location.City.CityName.Should().Be("Portland");
|
||||||
|
result.Location.City.StateProvince.Should().NotBeNull();
|
||||||
|
result.Location.City.StateProvince.StateProvinceName.Should().Be("Oregon");
|
||||||
|
result.Location.City.StateProvince.Country.Should().NotBeNull();
|
||||||
|
result.Location.City.StateProvince.Country.CountryName.Should().Be("United States");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetByIdAsync_ReturnsNull_WhenNotExists()
|
public async Task GetByIdAsync_ReturnsNull_WhenNotExists()
|
||||||
{
|
{
|
||||||
MockDbConnection conn = new();
|
MockDbConnection conn = new();
|
||||||
conn.Mocks.When(cmd => cmd.CommandText.Contains("INNER JOIN dbo.BreweryPostLocation"))
|
conn.Mocks.When(cmd => cmd.CommandText.Contains("LEFT JOIN dbo.BreweryPostLocation"))
|
||||||
.ReturnsTable(MockTable.Empty());
|
.ReturnsTable(MockTable.Empty());
|
||||||
BreweryRepository repo = CreateRepo(conn);
|
BreweryRepository repo = CreateRepo(conn);
|
||||||
BreweryPost? result = await repo.GetByIdAsync(Guid.NewGuid());
|
BreweryPost? result = await repo.GetByIdAsync(Guid.NewGuid());
|
||||||
@@ -225,7 +248,7 @@ public class BreweryRepositoryTests
|
|||||||
.ReturnsScalar(1);
|
.ReturnsScalar(1);
|
||||||
|
|
||||||
// Repository re-fetches the row after a successful update to return the new Timer.
|
// Repository re-fetches the row after a successful update to return the new Timer.
|
||||||
conn.Mocks.When(cmd => cmd.CommandText.Contains("INNER JOIN dbo.BreweryPostLocation"))
|
conn.Mocks.When(cmd => cmd.CommandText.Contains("LEFT JOIN dbo.BreweryPostLocation"))
|
||||||
.ReturnsTable(
|
.ReturnsTable(
|
||||||
MockTable
|
MockTable
|
||||||
.WithColumns(
|
.WithColumns(
|
||||||
@@ -241,7 +264,14 @@ public class BreweryRepositoryTests
|
|||||||
("AddressLine1", typeof(string)),
|
("AddressLine1", typeof(string)),
|
||||||
("AddressLine2", typeof(string)),
|
("AddressLine2", typeof(string)),
|
||||||
("PostalCode", typeof(string)),
|
("PostalCode", typeof(string)),
|
||||||
("Coordinates", typeof(byte[]))
|
("Coordinates", typeof(byte[])),
|
||||||
|
("CityName", typeof(string)),
|
||||||
|
("StateProvinceId", typeof(Guid)),
|
||||||
|
("StateProvinceName", typeof(string)),
|
||||||
|
("ISO3166_2", typeof(string)),
|
||||||
|
("CountryId", typeof(Guid)),
|
||||||
|
("CountryName", typeof(string)),
|
||||||
|
("ISO3166_1", typeof(string))
|
||||||
)
|
)
|
||||||
.AddRow(
|
.AddRow(
|
||||||
breweryId,
|
breweryId,
|
||||||
@@ -256,7 +286,14 @@ public class BreweryRepositoryTests
|
|||||||
"123 Main St",
|
"123 Main St",
|
||||||
null,
|
null,
|
||||||
"12345",
|
"12345",
|
||||||
null
|
null,
|
||||||
|
"Portland",
|
||||||
|
Guid.NewGuid(),
|
||||||
|
"Oregon",
|
||||||
|
"US-OR",
|
||||||
|
Guid.NewGuid(),
|
||||||
|
"United States",
|
||||||
|
"US"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,16 @@ namespace Features.Breweries.Dtos;
|
|||||||
public class BreweryLocationDto
|
public class BreweryLocationDto
|
||||||
{
|
{
|
||||||
public Guid BreweryPostLocationId { get; set; }
|
public Guid BreweryPostLocationId { get; set; }
|
||||||
|
|
||||||
public Guid BreweryPostId { get; set; }
|
|
||||||
|
|
||||||
public Guid CityId { get; set; }
|
public Guid CityId { get; set; }
|
||||||
|
public string CityName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string StateProvinceName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string StateProvinceCode { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string CountryName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string CountryCode { get; set; } = string.Empty;
|
||||||
|
|
||||||
public string AddressLine1 { get; set; } = string.Empty;
|
public string AddressLine1 { get; set; } = string.Empty;
|
||||||
|
|
||||||
@@ -41,3 +47,5 @@ public class BreweryDto
|
|||||||
/// <summary>Gets or sets the brewery's location, or <see langword="null"/> if none has been set.</summary>
|
/// <summary>Gets or sets the brewery's location, or <see langword="null"/> if none has been set.</summary>
|
||||||
public BreweryLocationDto? Location { get; set; }
|
public BreweryLocationDto? Location { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,12 @@ public static class BreweryDtoMapper
|
|||||||
: new BreweryLocationDto
|
: new BreweryLocationDto
|
||||||
{
|
{
|
||||||
BreweryPostLocationId = brewery.Location.BreweryPostLocationId,
|
BreweryPostLocationId = brewery.Location.BreweryPostLocationId,
|
||||||
BreweryPostId = brewery.Location.BreweryPostId,
|
|
||||||
CityId = brewery.Location.CityId,
|
CityId = brewery.Location.CityId,
|
||||||
|
CityName = brewery.Location.City?.CityName ?? string.Empty,
|
||||||
|
StateProvinceName = brewery.Location.City?.StateProvince?.StateProvinceName ?? string.Empty,
|
||||||
|
StateProvinceCode = brewery.Location.City?.StateProvince?.ISO3166_2 ?? string.Empty,
|
||||||
|
CountryName = brewery.Location.City?.StateProvince?.Country?.CountryName ?? string.Empty,
|
||||||
|
CountryCode = brewery.Location.City?.StateProvince?.Country?.ISO3166_1 ?? string.Empty,
|
||||||
AddressLine1 = brewery.Location.AddressLine1,
|
AddressLine1 = brewery.Location.AddressLine1,
|
||||||
AddressLine2 = brewery.Location.AddressLine2,
|
AddressLine2 = brewery.Location.AddressLine2,
|
||||||
PostalCode = brewery.Location.PostalCode,
|
PostalCode = brewery.Location.PostalCode,
|
||||||
|
|||||||
@@ -23,14 +23,45 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
|
|||||||
IBreweryRepository
|
IBreweryRepository
|
||||||
{
|
{
|
||||||
private const string SelectColumns =
|
private const string SelectColumns =
|
||||||
"bp.BreweryPostID, bp.PostedByID, bp.BreweryName, bp.Description, bp.CreatedAt, bp.UpdatedAt, bp.Timer, "
|
"""
|
||||||
+ "bpl.BreweryPostLocationID, bpl.CityID, bpl.AddressLine1, bpl.AddressLine2, bpl.PostalCode, bpl.Coordinates";
|
bp.BreweryPostID,
|
||||||
|
bp.PostedByID,
|
||||||
|
bp.BreweryName,
|
||||||
|
bp.Description,
|
||||||
|
bp.CreatedAt,
|
||||||
|
bp.UpdatedAt,
|
||||||
|
bp.Timer,
|
||||||
|
bpl.BreweryPostLocationID,
|
||||||
|
bpl.CityID,
|
||||||
|
bpl.AddressLine1,
|
||||||
|
bpl.AddressLine2,
|
||||||
|
bpl.PostalCode,
|
||||||
|
bpl.Coordinates,
|
||||||
|
c.CityName,
|
||||||
|
c.StateProvinceID,
|
||||||
|
sp.StateProvinceName,
|
||||||
|
sp.ISO3166_2,
|
||||||
|
sp.CountryID,
|
||||||
|
co.CountryName,
|
||||||
|
co.ISO3166_1
|
||||||
|
""";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Joins from <c>dbo.BreweryPost</c> down to <c>dbo.Country</c> so a row carries everything needed
|
||||||
|
/// to populate <see cref="BreweryPost.Location" />'s full <c>City</c> → <c>StateProvince</c> →
|
||||||
|
/// <c>Country</c> chain. All joins are <c>LEFT JOIN</c> (matching the previous stored procedure's
|
||||||
|
/// behavior), since a brewery post may have no location.
|
||||||
|
/// </summary>
|
||||||
|
private const string FromJoins =
|
||||||
|
"""
|
||||||
|
dbo.BreweryPost bp
|
||||||
|
LEFT JOIN dbo.BreweryPostLocation bpl ON bp.BreweryPostID = bpl.BreweryPostID
|
||||||
|
LEFT JOIN dbo.City c ON bpl.CityID = c.CityID
|
||||||
|
LEFT JOIN dbo.StateProvince sp ON c.StateProvinceID = sp.StateProvinceID
|
||||||
|
LEFT JOIN dbo.Country co ON sp.CountryID = co.CountryID
|
||||||
|
""";
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
/// <remarks>
|
|
||||||
/// Joins to the location table with an <c>INNER JOIN</c> (matching the previous stored procedure's
|
|
||||||
/// behavior), which is why a brewery with no location row is treated as not found.
|
|
||||||
/// </remarks>
|
|
||||||
public async Task<BreweryPost?> GetByIdAsync(Guid id)
|
public async Task<BreweryPost?> GetByIdAsync(Guid id)
|
||||||
{
|
{
|
||||||
await using DbConnection connection = await CreateConnection();
|
await using DbConnection connection = await CreateConnection();
|
||||||
@@ -38,8 +69,7 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
|
|||||||
command.CommandText =
|
command.CommandText =
|
||||||
$"""
|
$"""
|
||||||
SELECT {SelectColumns}
|
SELECT {SelectColumns}
|
||||||
FROM dbo.BreweryPost bp
|
FROM {FromJoins}
|
||||||
INNER JOIN dbo.BreweryPostLocation bpl ON bp.BreweryPostID = bpl.BreweryPostID
|
|
||||||
WHERE bp.BreweryPostID = @BreweryPostId
|
WHERE bp.BreweryPostID = @BreweryPostId
|
||||||
""";
|
""";
|
||||||
AddParameter(command, "@BreweryPostId", id);
|
AddParameter(command, "@BreweryPostId", id);
|
||||||
@@ -56,8 +86,7 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
|
|||||||
command.CommandText =
|
command.CommandText =
|
||||||
$"""
|
$"""
|
||||||
SELECT {SelectColumns}
|
SELECT {SelectColumns}
|
||||||
FROM dbo.BreweryPost bp
|
FROM {FromJoins}
|
||||||
LEFT JOIN dbo.BreweryPostLocation bpl ON bp.BreweryPostID = bpl.BreweryPostID
|
|
||||||
ORDER BY bp.CreatedAt DESC
|
ORDER BY bp.CreatedAt DESC
|
||||||
OFFSET @Offset ROWS FETCH NEXT @Limit ROWS ONLY
|
OFFSET @Offset ROWS FETCH NEXT @Limit ROWS ONLY
|
||||||
""";
|
""";
|
||||||
@@ -339,6 +368,7 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
|
|||||||
BreweryPostLocationId = reader.GetGuid(ordLocationId),
|
BreweryPostLocationId = reader.GetGuid(ordLocationId),
|
||||||
BreweryPostId = brewery.BreweryPostId,
|
BreweryPostId = brewery.BreweryPostId,
|
||||||
CityId = reader.GetGuid(reader.GetOrdinal("CityId")),
|
CityId = reader.GetGuid(reader.GetOrdinal("CityId")),
|
||||||
|
City = MapCity(reader),
|
||||||
AddressLine1 = reader.GetString(reader.GetOrdinal("AddressLine1")),
|
AddressLine1 = reader.GetString(reader.GetOrdinal("AddressLine1")),
|
||||||
AddressLine2 = reader.IsDBNull(ordAddressLine2)
|
AddressLine2 = reader.IsDBNull(ordAddressLine2)
|
||||||
? null
|
? null
|
||||||
@@ -353,6 +383,34 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
|
|||||||
return brewery;
|
return brewery;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps the current row's <c>City</c> → <c>StateProvince</c> → <c>Country</c> columns. The schema
|
||||||
|
/// enforces <c>NOT NULL</c> foreign keys down this whole chain, so whenever a location row was
|
||||||
|
/// joined in (the caller only calls this when it was), these columns are always populated too.
|
||||||
|
/// </summary>
|
||||||
|
private static City MapCity(DbDataReader reader)
|
||||||
|
{
|
||||||
|
return new City
|
||||||
|
{
|
||||||
|
CityId = reader.GetGuid(reader.GetOrdinal("CityId")),
|
||||||
|
CityName = reader.GetString(reader.GetOrdinal("CityName")),
|
||||||
|
StateProvinceId = reader.GetGuid(reader.GetOrdinal("StateProvinceId")),
|
||||||
|
StateProvince = new StateProvince
|
||||||
|
{
|
||||||
|
StateProvinceId = reader.GetGuid(reader.GetOrdinal("StateProvinceId")),
|
||||||
|
StateProvinceName = reader.GetString(reader.GetOrdinal("StateProvinceName")),
|
||||||
|
ISO3166_2 = reader.GetString(reader.GetOrdinal("ISO3166_2")),
|
||||||
|
CountryId = reader.GetGuid(reader.GetOrdinal("CountryId")),
|
||||||
|
Country = new Country
|
||||||
|
{
|
||||||
|
CountryId = reader.GetGuid(reader.GetOrdinal("CountryId")),
|
||||||
|
CountryName = reader.GetString(reader.GetOrdinal("CountryName")),
|
||||||
|
ISO3166_1 = reader.GetString(reader.GetOrdinal("ISO3166_1")),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private static void AddParameter(DbCommand command, string name, object? value)
|
private static void AddParameter(DbCommand command, string name, object? value)
|
||||||
{
|
{
|
||||||
DbParameter p = command.CreateParameter();
|
DbParameter p = command.CreateParameter();
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ services:
|
|||||||
sqlserver:
|
sqlserver:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
build:
|
build:
|
||||||
context: ./backend/Database
|
context: ./backend
|
||||||
dockerfile: Database.Migrations/Dockerfile
|
dockerfile: Database/Database.Migrations/Dockerfile
|
||||||
args:
|
args:
|
||||||
BUILD_CONFIGURATION: Release
|
BUILD_CONFIGURATION: Release
|
||||||
APP_UID: 1000
|
APP_UID: 1000
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ services:
|
|||||||
sqlserver:
|
sqlserver:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
build:
|
build:
|
||||||
context: ./backend/Database
|
context: ./backend
|
||||||
dockerfile: Database.Migrations/Dockerfile
|
dockerfile: Database/Database.Migrations/Dockerfile
|
||||||
args:
|
args:
|
||||||
BUILD_CONFIGURATION: Release
|
BUILD_CONFIGURATION: Release
|
||||||
APP_UID: 1000
|
APP_UID: 1000
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ services:
|
|||||||
sqlserver:
|
sqlserver:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
build:
|
build:
|
||||||
context: ./backend/Database
|
context: ./backend
|
||||||
dockerfile: Database.Migrations/Dockerfile
|
dockerfile: Database/Database.Migrations/Dockerfile
|
||||||
args:
|
args:
|
||||||
BUILD_CONFIGURATION: Release
|
BUILD_CONFIGURATION: Release
|
||||||
APP_UID: 1000
|
APP_UID: 1000
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ services:
|
|||||||
sqlserver:
|
sqlserver:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
build:
|
build:
|
||||||
context: ./backend/Database
|
context: ./backend
|
||||||
dockerfile: Database.Migrations/Dockerfile
|
dockerfile: Database/Database.Migrations/Dockerfile
|
||||||
args:
|
args:
|
||||||
BUILD_CONFIGURATION: Release
|
BUILD_CONFIGURATION: Release
|
||||||
APP_UID: 1000
|
APP_UID: 1000
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ services:
|
|||||||
sqlserver:
|
sqlserver:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
build:
|
build:
|
||||||
context: ./backend/Database
|
context: ./backend
|
||||||
dockerfile: Database.Migrations/Dockerfile
|
dockerfile: Database/Database.Migrations/Dockerfile
|
||||||
args:
|
args:
|
||||||
BUILD_CONFIGURATION: Release
|
BUILD_CONFIGURATION: Release
|
||||||
APP_UID: 1000
|
APP_UID: 1000
|
||||||
|
|||||||
Reference in New Issue
Block a user