using Gameboard.ShogiUI.Sockets.Repositories.CouchModels; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Text; using System.Threading.Tasks; using System.Web; namespace Gameboard.ShogiUI.Sockets.Repositories { public interface IGameboardRepository { Task CreateBoardState(Models.Session session); Task CreateSession(Models.SessionMetadata session); Task CreateUser(Models.User user); Task> ReadSessionMetadatas(); Task ReadGuestUser(Guid webSessionId); Task ReadSession(string name); Task ReadShogi(string name); Task UpdateSession(Models.SessionMetadata session); Task ReadSessionMetaData(string name); Task ReadUser(string userName); } public class GameboardRepository : IGameboardRepository { private const string ApplicationJson = "application/json"; private readonly HttpClient client; private readonly ILogger logger; public GameboardRepository(IHttpClientFactory clientFactory, ILogger logger) { client = clientFactory.CreateClient("couchdb"); this.logger = logger; } public async Task> ReadSessionMetadatas() { var selector = new Dictionary(2) { [nameof(SessionDocument.DocumentType)] = WhichDocumentType.Session }; var q = new { Selector = selector }; var content = new StringContent(JsonConvert.SerializeObject(q), Encoding.UTF8, ApplicationJson); var response = await client.PostAsync("_find", content); var responseContent = await response.Content.ReadAsStringAsync(); var results = JsonConvert.DeserializeObject>(responseContent); if (results != null) { return results .docs .Select(s => new Models.SessionMetadata(s.Name, s.IsPrivate, s.Player1, s.Player2)) .ToList(); } return new List(0); } public async Task ReadSession(string name) { var readShogiTask = ReadShogi(name); var response = await client.GetAsync(name); var responseContent = await response.Content.ReadAsStringAsync(); var couchModel = JsonConvert.DeserializeObject(responseContent); var shogi = await readShogiTask; if (shogi == null) { return null; } return couchModel.ToDomainModel(shogi); } public async Task ReadSessionMetaData(string name) { var response = await client.GetAsync(name); var responseContent = await response.Content.ReadAsStringAsync(); var couchModel = JsonConvert.DeserializeObject(responseContent); return couchModel.ToDomainModel(); } public async Task ReadShogi(string name) { var selector = new Dictionary(2) { [nameof(BoardStateDocument.DocumentType)] = WhichDocumentType.BoardState, [nameof(BoardStateDocument.Name)] = name }; var sort = new Dictionary(1) { [nameof(BoardStateDocument.CreatedDate)] = "asc" }; var query = JsonConvert.SerializeObject(new { selector, sort = new[] { sort } }); var content = new StringContent(query, Encoding.UTF8, ApplicationJson); var response = await client.PostAsync("_find", content); var responseContent = await response.Content.ReadAsStringAsync(); if (!response.IsSuccessStatusCode) { logger.LogError("Couch error during _find in {func}: {error}.\n\nQuery: {query}", nameof(ReadShogi), responseContent, query); return null; } var boardStates = JsonConvert .DeserializeObject>(responseContent) .docs; if (boardStates.Length == 0) return null; // Skip(1) because the first BoardState has no move; it represents the initial board state of a new Session. var moves = boardStates.Skip(1).Select(couchModel => { var move = couchModel.Move; Models.Move model = move!.PieceFromHand.HasValue ? new Models.Move(move.PieceFromHand.Value, move.To) : new Models.Move(move.From!, move.To, move.IsPromotion); return model; }).ToList(); return new Models.Shogi(moves); } /// /// Saves a snapshot of board state and the most recent move. /// public async Task CreateBoardState(Models.Session session) { var boardStateDocument = new BoardStateDocument(session.Name, session.Shogi); var content = new StringContent(JsonConvert.SerializeObject(boardStateDocument), Encoding.UTF8, ApplicationJson); var response = await client.PostAsync(string.Empty, content); return response.IsSuccessStatusCode; } public async Task CreateSession(Models.SessionMetadata session) { var sessionDocument = new SessionDocument(session); var sessionContent = new StringContent(JsonConvert.SerializeObject(sessionDocument), Encoding.UTF8, ApplicationJson); var postSessionDocumentTask = client.PostAsync(string.Empty, sessionContent); var boardStateDocument = new BoardStateDocument(session.Name, new Models.Shogi()); var boardStateContent = new StringContent(JsonConvert.SerializeObject(boardStateDocument), Encoding.UTF8, ApplicationJson); if ((await postSessionDocumentTask).IsSuccessStatusCode) { var response = await client.PostAsync(string.Empty, boardStateContent); return response.IsSuccessStatusCode; } return false; } public async Task UpdateSession(Models.SessionMetadata session) { var couchModel = new SessionDocument(session); var content = new StringContent(JsonConvert.SerializeObject(couchModel), Encoding.UTF8, ApplicationJson); var response = await client.PutAsync(couchModel.Id, content); return response.IsSuccessStatusCode; } //public async Task PutJoinPublicSession(PutJoinPublicSession request) //{ // var content = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, MediaType); // var response = await client.PutAsync(JoinSessionRoute, content); // var json = await response.Content.ReadAsStringAsync(); // return JsonConvert.DeserializeObject(json).JoinSucceeded; //} //public async Task PostJoinPrivateSession(PostJoinPrivateSession request) //{ // var content = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, MediaType); // var response = await client.PostAsync(JoinSessionRoute, content); // var json = await response.Content.ReadAsStringAsync(); // var deserialized = JsonConvert.DeserializeObject(json); // if (deserialized.JoinSucceeded) // { // return deserialized.SessionName; // } // return null; //} //public async Task> GetMoves(string gameName) //{ // var uri = $"Session/{gameName}/Moves"; // var get = await client.GetAsync(Uri.EscapeUriString(uri)); // var json = await get.Content.ReadAsStringAsync(); // if (string.IsNullOrWhiteSpace(json)) // { // return new List(); // } // var response = JsonConvert.DeserializeObject(json); // return response.Moves.Select(m => new Move(m)).ToList(); //} //public async Task PostMove(string gameName, PostMove request) //{ // var uri = $"Session/{gameName}/Move"; // var content = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, MediaType); // await client.PostAsync(Uri.EscapeUriString(uri), content); //} public async Task PostJoinCode(string gameName, string userName) { // var uri = $"JoinCode/{gameName}"; // var serialized = JsonConvert.SerializeObject(new PostJoinCode { PlayerName = userName }); // var content = new StringContent(serialized, Encoding.UTF8, MediaType); // var json = await (await client.PostAsync(Uri.EscapeUriString(uri), content)).Content.ReadAsStringAsync(); // return JsonConvert.DeserializeObject(json).JoinCode; return string.Empty; } public async Task ReadUser(string userName) { try { var document = new UserDocument(userName); var uri = new Uri(client.BaseAddress!, HttpUtility.UrlEncode(document.Id)); var response = await client.GetAsync(HttpUtility.UrlEncode(document.Id)); var response2 = await client.GetAsync(uri); var responseContent = await response.Content.ReadAsStringAsync(); if (response.IsSuccessStatusCode) { var user = JsonConvert.DeserializeObject(responseContent); return new Models.User(user.Name); } } catch (Exception e) { Console.WriteLine(e); } return null; } public async Task CreateUser(Models.User user) { var couchModel = new UserDocument(user.Name, user.WebSessionId); var content = new StringContent(JsonConvert.SerializeObject(couchModel), Encoding.UTF8, ApplicationJson); var response = await client.PostAsync(string.Empty, content); return response.IsSuccessStatusCode; } public async Task ReadGuestUser(Guid webSessionId) { var selector = new Dictionary(2) { [nameof(UserDocument.DocumentType)] = WhichDocumentType.User, [nameof(UserDocument.WebSessionId)] = webSessionId.ToString() }; var query = JsonConvert.SerializeObject(new { selector, limit = 1 }); var content = new StringContent(query, Encoding.UTF8, ApplicationJson); var response = await client.PostAsync("_find", content); var responseContent = await response.Content.ReadAsStringAsync(); if (!response.IsSuccessStatusCode) { logger.LogError("Couch error during _find in {func}: {error}.\n\nQuery: {query}", nameof(ReadGuestUser), responseContent, query); return null; } var result = JsonConvert.DeserializeObject>(responseContent); if (!string.IsNullOrWhiteSpace(result.warning)) { logger.LogError(new InvalidOperationException(result.warning), result.warning); return null; } var userDocument = result.docs.SingleOrDefault(); if (userDocument != null) { return new Models.User(userDocument.Name); } return null; } } }