revamping domain
This commit is contained in:
@@ -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 Player1 { get; set; } = string.Empty;
|
||||||
public string? Player2 { get; set; }
|
public string? Player2 { get; set; }
|
||||||
public string SessionName { get; set; }
|
public string SessionName { get; set; } = string.Empty;
|
||||||
|
public bool GameOver { get; set; }
|
||||||
public BoardState BoardState { get; set; }
|
public BoardState BoardState { get; set; }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,24 +1,14 @@
|
|||||||
CREATE PROCEDURE [session].[CreateSession]
|
CREATE PROCEDURE [session].[CreateSession]
|
||||||
@SessionName [session].[SessionName],
|
|
||||||
@Player1Name [user].[UserName],
|
|
||||||
@InitialBoardStateDocument [session].[JsonDocument]
|
@InitialBoardStateDocument [session].[JsonDocument]
|
||||||
AS
|
AS
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
||||||
SET NOCOUNT ON
|
SET NOCOUNT ON
|
||||||
SET XACT_ABORT ON
|
|
||||||
|
|
||||||
BEGIN TRANSACTION
|
INSERT INTO [session].[Session] ([Name], BoardState, Player1Id)
|
||||||
INSERT INTO [session].[Session] ([Name], Player1Id)
|
|
||||||
SELECT
|
SELECT
|
||||||
@SessionName,
|
JSON_VALUE(@InitialBoardStateDocument, '$.Name'),
|
||||||
|
@InitialBoardStateDocument,
|
||||||
Id
|
Id
|
||||||
FROM [user].[User]
|
FROM [user].[User]
|
||||||
WHERE [Name] = @Player1Name;
|
WHERE [Name] = JSON_VALUE(@InitialBoardStateDocument, '$.Player1');
|
||||||
|
|
||||||
INSERT INTO [session].[BoardState] (Document, SessionId)
|
|
||||||
VALUES
|
|
||||||
(@InitialBoardStateDocument, SCOPE_IDENTITY());
|
|
||||||
COMMIT
|
|
||||||
|
|
||||||
END
|
END
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
CREATE PROCEDURE [session].[ReadAllSessionsMetadata]
|
CREATE PROCEDURE [session].[ReadAllSessionsMetadata]
|
||||||
AS
|
AS
|
||||||
|
BEGIN
|
||||||
SET NOCOUNT ON;
|
SET NOCOUNT ON;
|
||||||
|
|
||||||
SELECT
|
SELECT
|
||||||
@@ -10,3 +10,4 @@ SELECT
|
|||||||
ELSE 2
|
ELSE 2
|
||||||
END AS PlayerCount
|
END AS PlayerCount
|
||||||
FROM [session].[Session];
|
FROM [session].[Session];
|
||||||
|
END
|
||||||
17
Shogi.Database/Session/Stored Procedures/ReadSession.sql
Normal file
17
Shogi.Database/Session/Stored Procedures/ReadSession.sql
Normal file
@@ -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
|
||||||
@@ -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
|
|
||||||
)
|
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
CREATE TABLE [session].[Session]
|
CREATE TABLE [session].[Session]
|
||||||
(
|
(
|
||||||
Id BIGINT NOT NULL PRIMARY KEY IDENTITY,
|
Id BIGINT NOT NULL PRIMARY KEY IDENTITY,
|
||||||
[Name] [session].[SessionName] NOT NULL UNIQUE,
|
|
||||||
Created DATETIMEOFFSET NOT NULL DEFAULT SYSDATETIMEOFFSET(),
|
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,
|
Player1Id BIGINT NOT NULL,
|
||||||
Player2Id BIGINT NULL,
|
Player2Id BIGINT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT [BoardState must be json] CHECK (isjson(DomainDocument)=1),
|
||||||
CONSTRAINT FK_Player1_User FOREIGN KEY (Player1Id) REFERENCES [user].[User] (Id)
|
CONSTRAINT FK_Player1_User FOREIGN KEY (Player1Id) REFERENCES [user].[User] (Id)
|
||||||
ON DELETE CASCADE
|
ON DELETE CASCADE
|
||||||
ON UPDATE CASCADE,
|
ON UPDATE CASCADE,
|
||||||
|
|||||||
@@ -71,7 +71,6 @@
|
|||||||
<Build Include="Session\session.sql" />
|
<Build Include="Session\session.sql" />
|
||||||
<Build Include="User\user.sql" />
|
<Build Include="User\user.sql" />
|
||||||
<Build Include="Session\Tables\Session.sql" />
|
<Build Include="Session\Tables\Session.sql" />
|
||||||
<Build Include="Session\Tables\BoardState.sql" />
|
|
||||||
<Build Include="Session\Stored Procedures\CreateSession.sql" />
|
<Build Include="Session\Stored Procedures\CreateSession.sql" />
|
||||||
<Build Include="Session\Stored Procedures\CreateBoardState.sql" />
|
<Build Include="Session\Stored Procedures\CreateBoardState.sql" />
|
||||||
<Build Include="User\Tables\User.sql" />
|
<Build Include="User\Tables\User.sql" />
|
||||||
@@ -84,6 +83,7 @@
|
|||||||
<Build Include="User\Tables\LoginPlatform.sql" />
|
<Build Include="User\Tables\LoginPlatform.sql" />
|
||||||
<None Include="Post Deployment\Scripts\PopulateLoginPlatforms.sql" />
|
<None Include="Post Deployment\Scripts\PopulateLoginPlatforms.sql" />
|
||||||
<Build Include="Session\Stored Procedures\UpdateSession.sql" />
|
<Build Include="Session\Stored Procedures\UpdateSession.sql" />
|
||||||
|
<Build Include="Session\Stored Procedures\ReadSession.sql" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PostDeploy Include="Post Deployment\Script.PostDeployment.sql" />
|
<PostDeploy Include="Post Deployment\Script.PostDeployment.sql" />
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ namespace Shogi.Domain;
|
|||||||
/// The board is always from Player1's perspective.
|
/// The board is always from Player1's perspective.
|
||||||
/// [0,0] is the lower-left position, [8,8] is the higher-right position
|
/// [0,0] is the lower-left position, [8,8] is the higher-right position
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class Session
|
public sealed class ShogiBoard
|
||||||
{
|
{
|
||||||
private readonly StandardRules rules;
|
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;
|
Name = name;
|
||||||
Player1 = player1;
|
Player1 = player1;
|
||||||
@@ -23,8 +23,6 @@ public sealed class Session
|
|||||||
|
|
||||||
public BoardState BoardState { get; }
|
public BoardState BoardState { get; }
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
public string Player1 { get; }
|
|
||||||
public string? Player2 { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Move a piece from a board position to another board position, potentially capturing an opponents piece. Respects all rules of the game.
|
/// Move a piece from a board position to another board position, potentially capturing an opponents piece. Respects all rules of the game.
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public class SessionController : ControllerBase
|
|||||||
{
|
{
|
||||||
var userId = User.GetShogiUserId();
|
var userId = User.GetShogiUserId();
|
||||||
if (string.IsNullOrWhiteSpace(userId)) return this.Unauthorized();
|
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
|
try
|
||||||
{
|
{
|
||||||
await sessionRepository.CreateSession(session);
|
await sessionRepository.CreateSession(session);
|
||||||
@@ -161,6 +161,13 @@ public class SessionController : ControllerBase
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("{name}")]
|
||||||
|
public async Task<ActionResult<ReadSessionResponse>> GetSession(string name)
|
||||||
|
{
|
||||||
|
await Task.CompletedTask;
|
||||||
|
return new ReadSessionResponse();
|
||||||
|
}
|
||||||
|
|
||||||
//[HttpPut("{sessionName}")]
|
//[HttpPut("{sessionName}")]
|
||||||
//public async Task<IActionResult> PutJoinSession([FromRoute] string sessionName)
|
//public async Task<IActionResult> PutJoinSession([FromRoute] string sessionName)
|
||||||
//{
|
//{
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System;
|
|||||||
|
|
||||||
namespace Shogi.Api.Repositories.CouchModels
|
namespace Shogi.Api.Repositories.CouchModels
|
||||||
{
|
{
|
||||||
|
[Obsolete]
|
||||||
public abstract class CouchDocument
|
public abstract class CouchDocument
|
||||||
{
|
{
|
||||||
[JsonProperty("_id")] public string Id { get; set; }
|
[JsonProperty("_id")] public string Id { get; set; }
|
||||||
|
|||||||
11
Shogi.Sockets/Repositories/Dto/SessionDto.cs
Normal file
11
Shogi.Sockets/Repositories/Dto/SessionDto.cs
Normal file
@@ -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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,9 +20,19 @@ public class QueryRepository : IQueryRespository
|
|||||||
"session.ReadAllSessionsMetadata",
|
"session.ReadAllSessionsMetadata",
|
||||||
commandType: System.Data.CommandType.StoredProcedure);
|
commandType: System.Data.CommandType.StoredProcedure);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<SessionMetadata?> ReadSession(string name)
|
||||||
|
{
|
||||||
|
using var connection = new SqlConnection(connectionString);
|
||||||
|
var results = await connection.QueryAsync<SessionMetadata>(
|
||||||
|
"session.ReadSession",
|
||||||
|
commandType: System.Data.CommandType.StoredProcedure);
|
||||||
|
return results.SingleOrDefault();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IQueryRespository
|
public interface IQueryRespository
|
||||||
{
|
{
|
||||||
Task<IEnumerable<SessionMetadata>> ReadAllSessionsMetadata();
|
Task<IEnumerable<SessionMetadata>> ReadAllSessionsMetadata();
|
||||||
|
Task<SessionMetadata?> ReadSession(string name);
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using Dapper;
|
using Dapper;
|
||||||
|
using Shogi.Api.Repositories.Dto;
|
||||||
using Shogi.Domain;
|
using Shogi.Domain;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Data.SqlClient;
|
using System.Data.SqlClient;
|
||||||
@@ -15,7 +16,7 @@ public class SessionRepository : ISessionRepository
|
|||||||
connectionString = configuration.GetConnectionString("ShogiDatabase");
|
connectionString = configuration.GetConnectionString("ShogiDatabase");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CreateSession(Session session)
|
public async Task CreateSession(ShogiBoard session, string player1)
|
||||||
{
|
{
|
||||||
var initialBoardState = JsonSerializer.Serialize(session.BoardState);
|
var initialBoardState = JsonSerializer.Serialize(session.BoardState);
|
||||||
using var connection = new SqlConnection(connectionString);
|
using var connection = new SqlConnection(connectionString);
|
||||||
@@ -24,14 +25,32 @@ public class SessionRepository : ISessionRepository
|
|||||||
new
|
new
|
||||||
{
|
{
|
||||||
SessionName = session.Name,
|
SessionName = session.Name,
|
||||||
Player1Name = session.Player1,
|
Player1Name = player1,
|
||||||
InitialBoardStateDocument = initialBoardState
|
InitialBoardStateDocument = initialBoardState
|
||||||
},
|
},
|
||||||
commandType: CommandType.StoredProcedure);
|
commandType: CommandType.StoredProcedure);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task ReadSession(string name)
|
||||||
|
{
|
||||||
|
using var connection = new SqlConnection(connectionString);
|
||||||
|
var results = await connection.QueryAsync<SessionDto>(
|
||||||
|
"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
|
public interface ISessionRepository
|
||||||
{
|
{
|
||||||
Task CreateSession(Session session);
|
Task CreateSession(ShogiBoard session);
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,17 @@
|
|||||||
<UserSecretsId>973a1f5f-ef25-4f1c-a24d-b0fc7d016ab8</UserSecretsId>
|
<UserSecretsId>973a1f5f-ef25-4f1c-a24d-b0fc7d016ab8</UserSecretsId>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Remove="Repositories\CouchModels\**" />
|
||||||
|
<Content Remove="Repositories\CouchModels\**" />
|
||||||
|
<EmbeddedResource Remove="Repositories\CouchModels\**" />
|
||||||
|
<None Remove="Repositories\CouchModels\**" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Remove="Repositories\GameboardRepository.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.2.2" />
|
<PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.2.2" />
|
||||||
<PackageReference Include="Azure.Identity" Version="1.6.1" />
|
<PackageReference Include="Azure.Identity" Version="1.6.1" />
|
||||||
|
|||||||
@@ -457,6 +457,6 @@ namespace Shogi.Domain.UnitTests
|
|||||||
board.InCheck.Should().Be(WhichPlayer.Player2);
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user