diff --git a/Shogi.Contracts/Api/Queries/ReadSessionResponse.cs b/Shogi.Contracts/Api/Queries/ReadSessionResponse.cs index f5e9191..e8300ab 100644 --- a/Shogi.Contracts/Api/Queries/ReadSessionResponse.cs +++ b/Shogi.Contracts/Api/Queries/ReadSessionResponse.cs @@ -2,7 +2,7 @@ namespace Shogi.Contracts.Api; - public class ReadSessionResponse - { - public Session Session { get; set; } - } +public class ReadSessionResponse +{ + public Session Session { get; set; } +} diff --git a/Shogi.Contracts/Types/Session.cs b/Shogi.Contracts/Types/Session.cs index d781feb..0a67ed1 100644 --- a/Shogi.Contracts/Types/Session.cs +++ b/Shogi.Contracts/Types/Session.cs @@ -1,10 +1,10 @@ -namespace Shogi.Contracts.Types +namespace Shogi.Contracts.Types; + +public class Session { - public class Session - { - public string Player1 { get; set; } - public string? Player2 { get; set; } - public string SessionName { get; set; } - public BoardState BoardState { get; set; } - } + public string Player1 { get; set; } = string.Empty; + public string? Player2 { get; set; } + public string SessionName { get; set; } = string.Empty; + public bool GameOver { get; set; } + public BoardState BoardState { get; set; } } diff --git a/Shogi.Database/Session/Stored Procedures/CreateSession.sql b/Shogi.Database/Session/Stored Procedures/CreateSession.sql index 8d68ac2..78cc9a8 100644 --- a/Shogi.Database/Session/Stored Procedures/CreateSession.sql +++ b/Shogi.Database/Session/Stored Procedures/CreateSession.sql @@ -1,24 +1,14 @@ CREATE PROCEDURE [session].[CreateSession] - @SessionName [session].[SessionName], - @Player1Name [user].[UserName], @InitialBoardStateDocument [session].[JsonDocument] AS BEGIN + SET NOCOUNT ON -SET NOCOUNT ON -SET XACT_ABORT ON - -BEGIN TRANSACTION - INSERT INTO [session].[Session] ([Name], Player1Id) + INSERT INTO [session].[Session] ([Name], BoardState, Player1Id) SELECT - @SessionName, + JSON_VALUE(@InitialBoardStateDocument, '$.Name'), + @InitialBoardStateDocument, Id FROM [user].[User] - WHERE [Name] = @Player1Name; - - INSERT INTO [session].[BoardState] (Document, SessionId) - VALUES - (@InitialBoardStateDocument, SCOPE_IDENTITY()); -COMMIT - + WHERE [Name] = JSON_VALUE(@InitialBoardStateDocument, '$.Player1'); END \ No newline at end of file diff --git a/Shogi.Database/Session/Stored Procedures/ReadAllSessionsMetadata.sql b/Shogi.Database/Session/Stored Procedures/ReadAllSessionsMetadata.sql index 2ff1c70..88252d5 100644 --- a/Shogi.Database/Session/Stored Procedures/ReadAllSessionsMetadata.sql +++ b/Shogi.Database/Session/Stored Procedures/ReadAllSessionsMetadata.sql @@ -1,12 +1,13 @@ CREATE PROCEDURE [session].[ReadAllSessionsMetadata] AS +BEGIN + SET NOCOUNT ON; -SET NOCOUNT ON; - -SELECT - [Name], - CASE - WHEN Player2Id IS NULL THEN 1 - ELSE 2 - END AS PlayerCount -FROM [session].[Session]; + SELECT + [Name], + CASE + WHEN Player2Id IS NULL THEN 1 + ELSE 2 + END AS PlayerCount + FROM [session].[Session]; +END \ No newline at end of file diff --git a/Shogi.Database/Session/Stored Procedures/ReadSession.sql b/Shogi.Database/Session/Stored Procedures/ReadSession.sql new file mode 100644 index 0000000..5bf4d8c --- /dev/null +++ b/Shogi.Database/Session/Stored Procedures/ReadSession.sql @@ -0,0 +1,17 @@ +CREATE PROCEDURE [session].[ReadSession] + @Name [session].[SessionName] +AS +BEGIN + SET NOCOUNT ON + + SELECT + sess.[Name], + GameOver, + BoardState, + p1.[Name] as Player1, + p2.[Name] as Player2 + FROM [session].[Session] sess + INNER JOIN [user].[User] p1 on sess.Player1Id = p1.Id + LEFT JOIN [user].[User] p2 on sess.Player2Id = p2.Id + WHERE sess.[Name] = @Name; +END diff --git a/Shogi.Database/Session/Tables/BoardState.sql b/Shogi.Database/Session/Tables/BoardState.sql deleted file mode 100644 index e55d176..0000000 --- a/Shogi.Database/Session/Tables/BoardState.sql +++ /dev/null @@ -1,10 +0,0 @@ -CREATE TABLE [session].[BoardState] -( - [Id] BIGINT NOT NULL PRIMARY KEY IDENTITY, - [Document] NVARCHAR(max) NOT NULL, - [SessionId] BIGINT NOT NULL, - - CONSTRAINT [Document must be json] CHECK (isjson(Document)=1), - CONSTRAINT FK_BoardState_Session FOREIGN KEY (SessionId) - REFERENCES [session].[Session] (Id) ON DELETE CASCADE ON UPDATE CASCADE -) diff --git a/Shogi.Database/Session/Tables/Session.sql b/Shogi.Database/Session/Tables/Session.sql index f570c64..0497464 100644 --- a/Shogi.Database/Session/Tables/Session.sql +++ b/Shogi.Database/Session/Tables/Session.sql @@ -1,12 +1,13 @@ CREATE TABLE [session].[Session] ( Id BIGINT NOT NULL PRIMARY KEY IDENTITY, - [Name] [session].[SessionName] NOT NULL UNIQUE, Created DATETIMEOFFSET NOT NULL DEFAULT SYSDATETIMEOFFSET(), - GameOver BIT NOT NULL DEFAULT 0, + DomainDocument [session].[JsonDocument] NOT NULL, + [Name] AS JSON_VALUE(DomainDocument, '$.Name') UNIQUE, Player1Id BIGINT NOT NULL, Player2Id BIGINT NULL, + CONSTRAINT [BoardState must be json] CHECK (isjson(DomainDocument)=1), CONSTRAINT FK_Player1_User FOREIGN KEY (Player1Id) REFERENCES [user].[User] (Id) ON DELETE CASCADE ON UPDATE CASCADE, diff --git a/Shogi.Database/Shogi.Database.sqlproj b/Shogi.Database/Shogi.Database.sqlproj index ea8b694..eee8fd4 100644 --- a/Shogi.Database/Shogi.Database.sqlproj +++ b/Shogi.Database/Shogi.Database.sqlproj @@ -71,7 +71,6 @@ - @@ -84,6 +83,7 @@ + diff --git a/Shogi.Domain/Aggregates/Session.cs b/Shogi.Domain/Aggregates/Session.cs index 5bd0afc..f5bf0b2 100644 --- a/Shogi.Domain/Aggregates/Session.cs +++ b/Shogi.Domain/Aggregates/Session.cs @@ -8,11 +8,11 @@ namespace Shogi.Domain; /// The board is always from Player1's perspective. /// [0,0] is the lower-left position, [8,8] is the higher-right position /// -public sealed class Session +public sealed class ShogiBoard { private readonly StandardRules rules; - public Session(string name, BoardState initialState, string player1, string? player2 = null) + public ShogiBoard(string name, BoardState initialState, string player1, string? player2 = null) { Name = name; Player1 = player1; @@ -23,8 +23,6 @@ public sealed class Session public BoardState BoardState { get; } public string Name { get; } - public string Player1 { get; } - public string? Player2 { get; } /// /// Move a piece from a board position to another board position, potentially capturing an opponents piece. Respects all rules of the game. diff --git a/Shogi.Sockets/Controllers/SessionController.cs b/Shogi.Sockets/Controllers/SessionController.cs index 865041f..c9993d3 100644 --- a/Shogi.Sockets/Controllers/SessionController.cs +++ b/Shogi.Sockets/Controllers/SessionController.cs @@ -37,7 +37,7 @@ public class SessionController : ControllerBase { var userId = User.GetShogiUserId(); if (string.IsNullOrWhiteSpace(userId)) return this.Unauthorized(); - var session = new Domain.Session(request.Name, Domain.BoardState.StandardStarting, userId); + var session = new Domain.ShogiBoard(request.Name, Domain.BoardState.StandardStarting, userId); try { await sessionRepository.CreateSession(session); @@ -161,6 +161,13 @@ public class SessionController : ControllerBase }); } + [HttpGet("{name}")] + public async Task> GetSession(string name) + { + await Task.CompletedTask; + return new ReadSessionResponse(); + } + //[HttpPut("{sessionName}")] //public async Task PutJoinSession([FromRoute] string sessionName) //{ diff --git a/Shogi.Sockets/Repositories/CouchModels/CouchDocument.cs b/Shogi.Sockets/Repositories/CouchModels/CouchDocument.cs index 95f743d..ecf2f41 100644 --- a/Shogi.Sockets/Repositories/CouchModels/CouchDocument.cs +++ b/Shogi.Sockets/Repositories/CouchModels/CouchDocument.cs @@ -3,24 +3,25 @@ using System; namespace Shogi.Api.Repositories.CouchModels { - public abstract class CouchDocument - { - [JsonProperty("_id")] public string Id { get; set; } - [JsonProperty("_rev")] public string? RevisionId { get; set; } - public WhichDocumentType DocumentType { get; } - public DateTimeOffset CreatedDate { get; set; } + [Obsolete] + public abstract class CouchDocument + { + [JsonProperty("_id")] public string Id { get; set; } + [JsonProperty("_rev")] public string? RevisionId { get; set; } + public WhichDocumentType DocumentType { get; } + public DateTimeOffset CreatedDate { get; set; } - public CouchDocument(WhichDocumentType documentType) - : this(string.Empty, documentType, DateTimeOffset.UtcNow) { } + public CouchDocument(WhichDocumentType documentType) + : this(string.Empty, documentType, DateTimeOffset.UtcNow) { } - public CouchDocument(string id, WhichDocumentType documentType) - : this(id, documentType, DateTimeOffset.UtcNow) { } + public CouchDocument(string id, WhichDocumentType documentType) + : this(id, documentType, DateTimeOffset.UtcNow) { } - public CouchDocument(string id, WhichDocumentType documentType, DateTimeOffset createdDate) - { - Id = id; - DocumentType = documentType; - CreatedDate = createdDate; - } - } + public CouchDocument(string id, WhichDocumentType documentType, DateTimeOffset createdDate) + { + Id = id; + DocumentType = documentType; + CreatedDate = createdDate; + } + } } diff --git a/Shogi.Sockets/Repositories/Dto/SessionDto.cs b/Shogi.Sockets/Repositories/Dto/SessionDto.cs new file mode 100644 index 0000000..7b70d5b --- /dev/null +++ b/Shogi.Sockets/Repositories/Dto/SessionDto.cs @@ -0,0 +1,11 @@ +namespace Shogi.Api.Repositories.Dto +{ + public class SessionDto + { + public string Name { get; set; } + public string Player1 { get; set; } + public string Player2 { get; set; } + public bool GameOver { get; set; } + public string BoardState { get; set; } + } +} diff --git a/Shogi.Sockets/Repositories/QueryRepository.cs b/Shogi.Sockets/Repositories/QueryRepository.cs index 14d0eaa..ca336dd 100644 --- a/Shogi.Sockets/Repositories/QueryRepository.cs +++ b/Shogi.Sockets/Repositories/QueryRepository.cs @@ -6,23 +6,33 @@ namespace Shogi.Api.Repositories; public class QueryRepository : IQueryRespository { - private readonly string connectionString; + private readonly string connectionString; - public QueryRepository(IConfiguration configuration) - { - connectionString = configuration.GetConnectionString("ShogiDatabase"); - } + public QueryRepository(IConfiguration configuration) + { + connectionString = configuration.GetConnectionString("ShogiDatabase"); + } - public async Task> ReadAllSessionsMetadata() - { - using var connection = new SqlConnection(connectionString); - return await connection.QueryAsync( - "session.ReadAllSessionsMetadata", - commandType: System.Data.CommandType.StoredProcedure); - } + public async Task> ReadAllSessionsMetadata() + { + using var connection = new SqlConnection(connectionString); + return await connection.QueryAsync( + "session.ReadAllSessionsMetadata", + commandType: System.Data.CommandType.StoredProcedure); + } + + public async Task ReadSession(string name) + { + using var connection = new SqlConnection(connectionString); + var results = await connection.QueryAsync( + "session.ReadSession", + commandType: System.Data.CommandType.StoredProcedure); + return results.SingleOrDefault(); + } } public interface IQueryRespository { - Task> ReadAllSessionsMetadata(); + Task> ReadAllSessionsMetadata(); + Task ReadSession(string name); } \ No newline at end of file diff --git a/Shogi.Sockets/Repositories/SessionRepository.cs b/Shogi.Sockets/Repositories/SessionRepository.cs index bf3570e..d7e2d9e 100644 --- a/Shogi.Sockets/Repositories/SessionRepository.cs +++ b/Shogi.Sockets/Repositories/SessionRepository.cs @@ -1,4 +1,5 @@ using Dapper; +using Shogi.Api.Repositories.Dto; using Shogi.Domain; using System.Data; using System.Data.SqlClient; @@ -8,30 +9,48 @@ namespace Shogi.Api.Repositories; public class SessionRepository : ISessionRepository { - private readonly string connectionString; + private readonly string connectionString; - public SessionRepository(IConfiguration configuration) - { - connectionString = configuration.GetConnectionString("ShogiDatabase"); - } + public SessionRepository(IConfiguration configuration) + { + connectionString = configuration.GetConnectionString("ShogiDatabase"); + } - public async Task CreateSession(Session session) - { - var initialBoardState = JsonSerializer.Serialize(session.BoardState); - using var connection = new SqlConnection(connectionString); - await connection.ExecuteAsync( - "session.CreateSession", - new - { - SessionName = session.Name, - Player1Name = session.Player1, - InitialBoardStateDocument = initialBoardState - }, - commandType: CommandType.StoredProcedure); - } + public async Task CreateSession(ShogiBoard session, string player1) + { + var initialBoardState = JsonSerializer.Serialize(session.BoardState); + using var connection = new SqlConnection(connectionString); + await connection.ExecuteAsync( + "session.CreateSession", + new + { + SessionName = session.Name, + Player1Name = player1, + InitialBoardStateDocument = initialBoardState + }, + commandType: CommandType.StoredProcedure); + } + + public async Task ReadSession(string name) + { + using var connection = new SqlConnection(connectionString); + var results = await connection.QueryAsync( + "session.ReadSession", + commandType: CommandType.StoredProcedure); + + if (!results.Any()) + { + return null; + } + + var dto = results.First(); + return new Session( + name: dto.Name, + initialState: JsonSerializer.Deserialize< dto.BoardState) + } } public interface ISessionRepository { - Task CreateSession(Session session); + Task CreateSession(ShogiBoard session); } \ No newline at end of file diff --git a/Shogi.Sockets/Shogi.Api.csproj b/Shogi.Sockets/Shogi.Api.csproj index 0566d83..bf92b1c 100644 --- a/Shogi.Sockets/Shogi.Api.csproj +++ b/Shogi.Sockets/Shogi.Api.csproj @@ -11,6 +11,17 @@ 973a1f5f-ef25-4f1c-a24d-b0fc7d016ab8 + + + + + + + + + + + diff --git a/Tests/UnitTests/ShogiShould.cs b/Tests/UnitTests/ShogiShould.cs index 7ac9993..946dbed 100644 --- a/Tests/UnitTests/ShogiShould.cs +++ b/Tests/UnitTests/ShogiShould.cs @@ -457,6 +457,6 @@ namespace Shogi.Domain.UnitTests board.InCheck.Should().Be(WhichPlayer.Player2); } - private static Session MockSession() => new Session("Test Session", BoardState.StandardStarting, "Test P1", "Test P2"); + private static ShogiBoard MockSession() => new ShogiBoard("Test Session", BoardState.StandardStarting, "Test P1", "Test P2"); } }