diff --git a/RabinChess.Server/.gitignore b/RabinChess.Server/.gitignore index c6e652a..efb6e12 100644 --- a/RabinChess.Server/.gitignore +++ b/RabinChess.Server/.gitignore @@ -210,3 +210,9 @@ FakesAssemblies/ GeneratedArtifacts/ _Pvt_Extensions/ ModelManifest.xml + +# Config files +**/connectionStrings.config + +# Publish profiles +RabinChess.Server.API/Properties/PublishProfiles/ diff --git a/RabinChess.Server/RabinChess.Server.API/App_Start/Startup.Auth.cs b/RabinChess.Server/RabinChess.Server.API/App_Start/Startup.Auth.cs new file mode 100644 index 0000000..f6d6849 --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.API/App_Start/Startup.Auth.cs @@ -0,0 +1,51 @@ +using System; +using Microsoft.AspNet.Identity; +using Microsoft.Owin; +using Microsoft.Owin.Cors; +using Microsoft.Owin.Security.Cookies; +using Microsoft.Owin.Security.OAuth; +using Owin; +using RabinChess.Server.API.Models; +using RabinChess.Server.API.Providers; +using RabinChess.Server.API.Stores; + +namespace RabinChess.Server.API +{ + public partial class Startup + { + public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; } + + public static string PublicClientId { get; private set; } + + public static Func> UserManagerFactory { get; private set; } + + static Startup() + { + PublicClientId = "self"; + + UserManagerFactory = + () => new UserManager(new UserStore()) {PasswordHasher = new Security.PasswordHasher()}; + + OAuthOptions = new OAuthAuthorizationServerOptions + { + TokenEndpointPath = new PathString("/api/token"), + Provider = new ApplicationOAuthProvider(PublicClientId, UserManagerFactory), + AccessTokenExpireTimeSpan = TimeSpan.FromDays(14), + AllowInsecureHttp = true + }; + } + + // For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864 + public void ConfigureAuth(IAppBuilder app) + { + app.UseCors(CorsOptions.AllowAll); + // Enable the application to use a cookie to store information for the signed in user + // and to use a cookie to temporarily store information about a user logging in with a third party login provider + app.UseCookieAuthentication(new CookieAuthenticationOptions()); + app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie); + + // Enable the application to use bearer tokens to authenticate users + app.UseOAuthBearerTokens(OAuthOptions); + } + } +} \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.API/Controllers/AccountController.cs b/RabinChess.Server/RabinChess.Server.API/Controllers/AccountController.cs new file mode 100644 index 0000000..d80e9be --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.API/Controllers/AccountController.cs @@ -0,0 +1,79 @@ +using System.Net.Http; +using System.Web.Http; +using Microsoft.AspNet.Identity; +using Microsoft.Owin.Security; +using Microsoft.Owin.Security.Cookies; +using RabinChess.Server.API.Models; + +namespace RabinChess.Server.API.Controllers +{ + [RoutePrefix("api/Account")] + public class AccountController : ApiController + { + public UserManager UserManager { get; private set; } + public ISecureDataFormat AccessTokenFormat { get; private set; } + + public AccountController() : this(Startup.UserManagerFactory(), Startup.OAuthOptions.AccessTokenFormat) + { + } + + public AccountController(UserManager userManager, + ISecureDataFormat accessTokenFormat) + { + UserManager = userManager; + AccessTokenFormat = accessTokenFormat; + } + + [HttpPost] + [Route("Logout")] + public IHttpActionResult Logout() + { + Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType); + return Ok(); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + UserManager.Dispose(); + } + + base.Dispose(disposing); + } + + private IAuthenticationManager Authentication + { + get { return Request.GetOwinContext().Authentication; } + } + + private IHttpActionResult GetErrorResult(IdentityResult result) + { + if (result == null) + { + return InternalServerError(); + } + + if (!result.Succeeded) + { + if (result.Errors != null) + { + foreach (string error in result.Errors) + { + ModelState.AddModelError("", error); + } + } + + if (ModelState.IsValid) + { + // No ModelState errors are available to send, so just return an empty BadRequest. + return BadRequest(); + } + + return BadRequest(ModelState); + } + + return null; + } + } +} diff --git a/RabinChess.Server/RabinChess.Server.API/Controllers/GamesController.cs b/RabinChess.Server/RabinChess.Server.API/Controllers/GamesController.cs new file mode 100644 index 0000000..7045dbd --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.API/Controllers/GamesController.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Linq; +using System.Web.Http; +using Microsoft.AspNet.Identity; +using RabinChess.Server.API.Models; +using RubinChess.Server.Logic; + +namespace RabinChess.Server.API.Controllers +{ + [Route("api/Games")] + public class GamesController : ApiController + { + [Route("api/Games")] + [HttpGet] + public List Get() + { + return ContextFactory.GetGamesContext().GetGames(User.Identity.GetUserId()).Select(x => (GameListItemViewModel) x).ToList(); + } + } +} diff --git a/RabinChess.Server/RabinChess.Server.API/Controllers/SampleController.cs b/RabinChess.Server/RabinChess.Server.API/Controllers/SampleController.cs deleted file mode 100644 index a749bb5..0000000 --- a/RabinChess.Server/RabinChess.Server.API/Controllers/SampleController.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Web.Http; - -namespace RabinChess.Server.API.Controllers -{ - public class SampleController : ApiController - { - // GET: api/Sample - public IEnumerable Get() - { - return new string[] { "value1", "value2" }; - } - - // GET: api/Sample?id=5 - public string Get([FromUri]int id) - { - return "value"; - } - - // POST: api/Sample - public void Post([FromBody]string value) - { - } - - // PUT: api/Sample - public void Put([FromBody]string value) - { - } - - // DELETE: api/Sample?id=5 - public void Delete([FromUri]int id) - { - } - } -} diff --git a/RabinChess.Server/RabinChess.Server.API/Models/GameListItemViewModel.cs b/RabinChess.Server/RabinChess.Server.API/Models/GameListItemViewModel.cs new file mode 100644 index 0000000..c91a5c5 --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.API/Models/GameListItemViewModel.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using RabinChess.Server.DataStructures; + +namespace RabinChess.Server.API.Models +{ + public class GameListItemViewModel + { + public string Name { get; set; } + public string Tags { get; set; } + public Guid Id { get; set; } + + public static explicit operator GameListItemViewModel(GameListItemVM model) + { + return new GameListItemViewModel + { + Id = model.Id, + Name = model.Name, + Tags = model.Tags + }; + } + } +} \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.API/Models/UserModel.cs b/RabinChess.Server/RabinChess.Server.API/Models/UserModel.cs new file mode 100644 index 0000000..505a1ea --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.API/Models/UserModel.cs @@ -0,0 +1,41 @@ +using Microsoft.AspNet.Identity; +using RubinChess.Server.Database.Entities; + +namespace RabinChess.Server.API.Models +{ + public class UserModel : IUser + { + public int Id { get; private set; } + public string UserName { get; set; } + public string PasswordHash { get; set; } + public string Email { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + + public static explicit operator UserModel(User user) + { + return new UserModel + { + Email = user.Email, + FirstName = user.FirstName, + LastName = user.LastName, + Id = user.Id, + PasswordHash = user.PasswordHash, + UserName = user.UserName + }; + } + + public static explicit operator User(UserModel user) + { + return new User + { + Email = user.Email, + FirstName = user.FirstName, + LastName = user.LastName, + Id = user.Id, + PasswordHash = user.PasswordHash, + UserName = user.UserName + }; + } + } +} \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.API/Properties/AssemblyInfo.cs b/RabinChess.Server/RabinChess.Server.API/Properties/AssemblyInfo.cs index 1b8e1fc..bacfa22 100644 --- a/RabinChess.Server/RabinChess.Server.API/Properties/AssemblyInfo.cs +++ b/RabinChess.Server/RabinChess.Server.API/Properties/AssemblyInfo.cs @@ -1,6 +1,7 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using Microsoft.Owin; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information @@ -33,3 +34,6 @@ // by using the '*' as shown below: [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] + +// Additional assemblies +[assembly: OwinStartup(typeof(RabinChess.Server.API.Startup))] diff --git a/RabinChess.Server/RabinChess.Server.API/Providers/ApplicationOAuthProvider.cs b/RabinChess.Server/RabinChess.Server.API/Providers/ApplicationOAuthProvider.cs new file mode 100644 index 0000000..ead0888 --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.API/Providers/ApplicationOAuthProvider.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNet.Identity; +using Microsoft.Owin.Security; +using Microsoft.Owin.Security.Cookies; +using Microsoft.Owin.Security.OAuth; +using RabinChess.Server.API.Models; + +namespace RabinChess.Server.API.Providers +{ + public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider + { + private readonly string _publicClientId; + private readonly Func> _userManagerFactory; + + public ApplicationOAuthProvider(string publicClientId, Func> userManagerFactory) + { + if (publicClientId == null) + { + throw new ArgumentNullException("publicClientId"); + } + + if (userManagerFactory == null) + { + throw new ArgumentNullException("userManagerFactory"); + } + + _publicClientId = publicClientId; + _userManagerFactory = userManagerFactory; + } + + public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) + { + using (UserManager userManager = _userManagerFactory()) + { + UserModel user = await userManager.FindAsync(context.UserName, context.Password); + + if (user == null) + { + context.SetError("invalid_grant", "The user name or password is incorrect."); + return; + } + + ClaimsIdentity oAuthIdentity = await userManager.CreateIdentityAsync(user, + context.Options.AuthenticationType); + ClaimsIdentity cookiesIdentity = await userManager.CreateIdentityAsync(user, + CookieAuthenticationDefaults.AuthenticationType); + AuthenticationProperties properties = CreateProperties(user.UserName); + AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties); + context.Validated(ticket); + context.Request.Context.Authentication.SignIn(cookiesIdentity); + } + } + + public override Task TokenEndpoint(OAuthTokenEndpointContext context) + { + foreach (KeyValuePair property in context.Properties.Dictionary) + { + context.AdditionalResponseParameters.Add(property.Key, property.Value); + } + + return Task.FromResult(null); + } + + public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) + { + // Resource owner password credentials does not provide a client ID. + if (context.ClientId == null) + { + context.Validated(); + } + + return Task.FromResult(null); + } + + public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context) + { + if (context.ClientId == _publicClientId) + { + Uri expectedRootUri = new Uri(context.Request.Uri, "/"); + + if (expectedRootUri.AbsoluteUri == context.RedirectUri) + { + context.Validated(); + } + } + + return Task.FromResult(null); + } + + public static AuthenticationProperties CreateProperties(string userName) + { + IDictionary data = new Dictionary + { + { "userName", userName } + }; + return new AuthenticationProperties(data); + } + } +} \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.API/RabinChess.Server.API.csproj b/RabinChess.Server/RabinChess.Server.API/RabinChess.Server.API.csproj index c1c3c3d..3807821 100644 --- a/RabinChess.Server/RabinChess.Server.API/RabinChess.Server.API.csproj +++ b/RabinChess.Server/RabinChess.Server.API/RabinChess.Server.API.csproj @@ -38,8 +38,60 @@ 4 + + ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll + True + + + ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll + True + + + ..\packages\Microsoft.AspNet.Identity.Core.2.2.1\lib\net45\Microsoft.AspNet.Identity.Core.dll + True + + + ..\packages\Microsoft.AspNet.Identity.EntityFramework.2.2.1\lib\net45\Microsoft.AspNet.Identity.EntityFramework.dll + True + + + ..\packages\Microsoft.AspNet.Identity.Owin.2.2.1\lib\net45\Microsoft.AspNet.Identity.Owin.dll + True + + + ..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll + True + + + ..\packages\Microsoft.Owin.Cors.3.0.1\lib\net45\Microsoft.Owin.Cors.dll + True + + + ..\packages\Microsoft.Owin.Host.SystemWeb.3.0.1\lib\net45\Microsoft.Owin.Host.SystemWeb.dll + True + + + ..\packages\Microsoft.Owin.Security.2.1.0\lib\net45\Microsoft.Owin.Security.dll + True + + + ..\packages\Microsoft.Owin.Security.Cookies.2.1.0\lib\net45\Microsoft.Owin.Security.Cookies.dll + True + + + ..\packages\Microsoft.Owin.Security.OAuth.2.1.0\lib\net45\Microsoft.Owin.Security.OAuth.dll + True + + + ..\packages\Owin.1.0\lib\net40\Owin.dll + True + + + ..\packages\Microsoft.AspNet.Cors.5.0.0\lib\net45\System.Web.Cors.dll + True + @@ -49,6 +101,10 @@ + + ..\packages\Microsoft.AspNet.WebApi.Owin.5.2.3\lib\net45\System.Web.Http.Owin.dll + True + @@ -77,14 +133,25 @@ - + + Global.asax + + + + + + + + PreserveNewest + + Web.config @@ -97,6 +164,10 @@ {28073fba-2df2-4df6-a519-e4ddff3cded9} RabinChess.Server.Database + + {3d934dc5-2d2b-496c-aa69-17bc7a7d07a5} + RabinChess.Server.DataStructures + {07351139-6d20-4748-a41d-6b0cfe0086b2} RabinChess.Server.Logic @@ -104,7 +175,6 @@ - 10.0 diff --git a/RabinChess.Server/RabinChess.Server.API/Security/PasswordHasher.cs b/RabinChess.Server/RabinChess.Server.API/Security/PasswordHasher.cs new file mode 100644 index 0000000..12348f6 --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.API/Security/PasswordHasher.cs @@ -0,0 +1,25 @@ +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using Microsoft.AspNet.Identity; + +namespace RabinChess.Server.API.Security +{ + public class PasswordHasher : IPasswordHasher + { + public string HashPassword(string password) + { + var x = new MD5CryptoServiceProvider(); + byte[] passwordBytes = Encoding.ASCII.GetBytes(password); + passwordBytes = x.ComputeHash(passwordBytes); + return passwordBytes.Aggregate("", (current, passwordByte) => current + passwordByte.ToString("x2").ToLower()); + } + + public PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword) + { + return hashedPassword == HashPassword(providedPassword) + ? PasswordVerificationResult.Success + : PasswordVerificationResult.Failed; + } + } +} \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.API/Startup.cs b/RabinChess.Server/RabinChess.Server.API/Startup.cs new file mode 100644 index 0000000..6577502 --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.API/Startup.cs @@ -0,0 +1,12 @@ +using Owin; + +namespace RabinChess.Server.API +{ + public partial class Startup + { + public void Configuration(IAppBuilder app) + { + ConfigureAuth(app); + } + } +} \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.API/Stores/UserStore.cs b/RabinChess.Server/RabinChess.Server.API/Stores/UserStore.cs new file mode 100644 index 0000000..20740fc --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.API/Stores/UserStore.cs @@ -0,0 +1,60 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNet.Identity; +using RabinChess.Server.API.Models; +using RubinChess.Server.Database.Entities; +using RubinChess.Server.Logic; + +namespace RabinChess.Server.API.Stores +{ + public class UserStore : IUserPasswordStore + { + public void Dispose() + { + + } + + public Task CreateAsync(UserModel user) + { + return Task.FromResult(ContextFactory.GetUserContext().AddUser((User) user)); + } + + public Task UpdateAsync(UserModel user) + { + return Task.FromResult(ContextFactory.GetUserContext().UpdateUser((User) user)); + } + + public Task DeleteAsync(UserModel user) + { + return Task.FromResult(ContextFactory.GetUserContext().DeleteUser(user.Id)); + } + + public Task FindByIdAsync(int userId) + { + return Task.FromResult((UserModel) ContextFactory.GetUserContext().GetUser(userId)); + } + + public Task FindByNameAsync(string userName) + { + return Task.FromResult((UserModel) ContextFactory.GetUserContext().GetUser(userName)); + } + + public Task SetPasswordHashAsync(UserModel user, string passwordHash) + { + user.PasswordHash = passwordHash; + return Task.FromResult(0); + } + + public Task GetPasswordHashAsync(UserModel user) + { + if(user == null) + throw new ArgumentNullException("user"); + return Task.FromResult(user.PasswordHash); + } + + public Task HasPasswordAsync(UserModel user) + { + return Task.FromResult(user.PasswordHash != null); + } + } +} \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.API/Web.config b/RabinChess.Server/RabinChess.Server.API/Web.config index 1a89eac..efef9ef 100644 --- a/RabinChess.Server/RabinChess.Server.API/Web.config +++ b/RabinChess.Server/RabinChess.Server.API/Web.config @@ -4,6 +4,20 @@ http://go.microsoft.com/fwlink/?LinkId=301879 --> + + +
+ + + + + + + + + + + @@ -31,6 +45,15 @@ + + + + + + + + + diff --git a/RabinChess.Server/RabinChess.Server.API/packages.config b/RabinChess.Server/RabinChess.Server.API/packages.config index f2ea303..c37448d 100644 --- a/RabinChess.Server/RabinChess.Server.API/packages.config +++ b/RabinChess.Server/RabinChess.Server.API/packages.config @@ -1,8 +1,21 @@  + + + + + + + + + + + + + \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.DataStructures/GameListItemVM.cs b/RabinChess.Server/RabinChess.Server.DataStructures/GameListItemVM.cs new file mode 100644 index 0000000..535e924 --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.DataStructures/GameListItemVM.cs @@ -0,0 +1,11 @@ +using System; + +namespace RabinChess.Server.DataStructures +{ + public class GameListItemVM + { + public string Name { get; set; } + public string Tags { get; set; } + public Guid Id { get; set; } + } +} diff --git a/RabinChess.Server/RabinChess.Server.DataStructures/GameTagVM.cs b/RabinChess.Server/RabinChess.Server.DataStructures/GameTagVM.cs new file mode 100644 index 0000000..77819b7 --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.DataStructures/GameTagVM.cs @@ -0,0 +1,32 @@ +using System; +using RubinChess.Server.Database.Entities; + +namespace RabinChess.Server.DataStructures +{ + public class GameTagVM + { + public string Name { get; set; } + public string Value { get; set; } + public Guid GameId { get; set; } + + public static explicit operator GameTagVM(GameTag model) + { + return new GameTagVM + { + GameId = model.GameId, + Name = model.Name, + Value = model.Value + }; + } + + public static explicit operator GameTag(GameTagVM model) + { + return new GameTag + { + GameId = model.GameId, + Name = model.Name, + Value = model.Value + }; + } + } +} \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.DataStructures/GameVM.cs b/RabinChess.Server/RabinChess.Server.DataStructures/GameVM.cs new file mode 100644 index 0000000..485e56c --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.DataStructures/GameVM.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using RubinChess.Server.Database.Entities; + +namespace RabinChess.Server.DataStructures +{ + public class GameVM + { + public string Name { get; set; } + public string Notation { get; set; } + public List Tags { get; set; } + public int UserId { get; set; } + public Guid Id { get; set; } + + public static explicit operator GameVM(Game model) + { + return new GameVM + { + Name = model.Name, + Id = model.Id, + Notation = model.GameNotation, + Tags = model.Tags.Select(x => (GameTagVM) x).ToList(), + UserId = model.UserId + }; + } + + public static explicit operator Game(GameVM model) + { + return new Game + { + GameNotation = model.Notation, + Id = model.Id, + Name = model.Name, + Tags = model.Tags.Select(x => (GameTag) x).ToList(), + UserId = model.UserId + }; + } + } +} \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.DataStructures/Properties/AssemblyInfo.cs b/RabinChess.Server/RabinChess.Server.DataStructures/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..d8a535a --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.DataStructures/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("RabinChess.Server.DataStructures")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("RabinChess.Server.DataStructures")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("49ed3b65-a78d-463a-a288-cb995e3bc713")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/RabinChess.Server/RabinChess.Server.DataStructures/RabinChess.Server.DataStructures.csproj b/RabinChess.Server/RabinChess.Server.DataStructures/RabinChess.Server.DataStructures.csproj new file mode 100644 index 0000000..76b4bb0 --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.DataStructures/RabinChess.Server.DataStructures.csproj @@ -0,0 +1,61 @@ + + + + + Debug + AnyCPU + {3D934DC5-2D2B-496C-AA69-17BC7A7D07A5} + Library + Properties + RabinChess.Server.DataStructures + RabinChess.Server.DataStructures + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + {28073fba-2df2-4df6-a519-e4ddff3cded9} + RabinChess.Server.Database + + + + + \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.Database/App.config b/RabinChess.Server/RabinChess.Server.Database/App.config new file mode 100644 index 0000000..e54f19e --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.Database/App.config @@ -0,0 +1,18 @@ + + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.Database/Entities/Game.cs b/RabinChess.Server/RabinChess.Server.Database/Entities/Game.cs new file mode 100644 index 0000000..4a839d2 --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.Database/Entities/Game.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace RubinChess.Server.Database.Entities +{ + public class Game + { + [Key] + public Guid Id { get; set; } + + public User User { get; set; } + + [ForeignKey("User"), Required] + public int UserId { get; set; } + + [Required] + public string GameNotation { get; set; } + + public string Name { get; set; } + + public virtual List Tags { get; set; } + } +} \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.Database/Entities/GameTag.cs b/RabinChess.Server/RabinChess.Server.Database/Entities/GameTag.cs new file mode 100644 index 0000000..2614669 --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.Database/Entities/GameTag.cs @@ -0,0 +1,20 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace RubinChess.Server.Database.Entities +{ + public class GameTag + { + public Game Game { get; set; } + + [Key, ForeignKey("Game"), Column(Order = 1)] + public Guid GameId { get; set; } + + [Key, Column(Order = 2)] + public string Name { get; set; } + + [Required] + public string Value { get; set; } + } +} \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.Database/Entities/User.cs b/RabinChess.Server/RabinChess.Server.Database/Entities/User.cs new file mode 100644 index 0000000..f8f6b53 --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.Database/Entities/User.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace RubinChess.Server.Database.Entities +{ + public class User + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; set; } + + [MinLength(5), MaxLength(25), Required] + public string UserName { get; set; } + + [Required] + public string PasswordHash { get; set; } + + [Required] + public string Email { get; set; } + + [Required, MaxLength(25)] + public string FirstName { get; set; } + + [Required, MaxLength(40)] + public string LastName { get; set; } + + public virtual List Games { get; set; } + } +} \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.Database/Migrations/201605031506553_InitialCreate.Designer.cs b/RabinChess.Server/RabinChess.Server.Database/Migrations/201605031506553_InitialCreate.Designer.cs new file mode 100644 index 0000000..c5c28d9 --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.Database/Migrations/201605031506553_InitialCreate.Designer.cs @@ -0,0 +1,29 @@ +// +namespace RubinChess.Server.Database.Migrations +{ + using System.CodeDom.Compiler; + using System.Data.Entity.Migrations; + using System.Data.Entity.Migrations.Infrastructure; + using System.Resources; + + [GeneratedCode("EntityFramework.Migrations", "6.1.3-40302")] + public sealed partial class InitialCreate : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(InitialCreate)); + + string IMigrationMetadata.Id + { + get { return "201605031506553_InitialCreate"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/RabinChess.Server/RabinChess.Server.Database/Migrations/201605031506553_InitialCreate.cs b/RabinChess.Server/RabinChess.Server.Database/Migrations/201605031506553_InitialCreate.cs new file mode 100644 index 0000000..86b574f --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.Database/Migrations/201605031506553_InitialCreate.cs @@ -0,0 +1,61 @@ +namespace RubinChess.Server.Database.Migrations +{ + using System; + using System.Data.Entity.Migrations; + + public partial class InitialCreate : DbMigration + { + public override void Up() + { + CreateTable( + "dbo.Games", + c => new + { + Id = c.Guid(nullable: false), + UserId = c.Int(nullable: false), + GameNotation = c.String(nullable: false), + Name = c.String(), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Users", t => t.UserId, cascadeDelete: true) + .Index(t => t.UserId); + + CreateTable( + "dbo.GameTags", + c => new + { + GameId = c.Guid(nullable: false), + Name = c.String(nullable: false, maxLength: 128), + Value = c.String(nullable: false), + }) + .PrimaryKey(t => new { t.GameId, t.Name }) + .ForeignKey("dbo.Games", t => t.GameId, cascadeDelete: true) + .Index(t => t.GameId); + + CreateTable( + "dbo.Users", + c => new + { + Id = c.Int(nullable: false, identity: true), + UserName = c.String(nullable: false, maxLength: 25), + PasswordHash = c.String(nullable: false), + Email = c.String(nullable: false), + FirstName = c.String(nullable: false, maxLength: 25), + LastName = c.String(nullable: false, maxLength: 40), + }) + .PrimaryKey(t => t.Id); + + } + + public override void Down() + { + DropForeignKey("dbo.Games", "UserId", "dbo.Users"); + DropForeignKey("dbo.GameTags", "GameId", "dbo.Games"); + DropIndex("dbo.GameTags", new[] { "GameId" }); + DropIndex("dbo.Games", new[] { "UserId" }); + DropTable("dbo.Users"); + DropTable("dbo.GameTags"); + DropTable("dbo.Games"); + } + } +} diff --git a/RabinChess.Server/RabinChess.Server.Database/Migrations/201605031506553_InitialCreate.resx b/RabinChess.Server/RabinChess.Server.Database/Migrations/201605031506553_InitialCreate.resx new file mode 100644 index 0000000..d2e4df2 --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.Database/Migrations/201605031506553_InitialCreate.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 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 + + + dbo + + \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.Database/Migrations/201605171858497_UserIdAutoGenerate.Designer.cs b/RabinChess.Server/RabinChess.Server.Database/Migrations/201605171858497_UserIdAutoGenerate.Designer.cs new file mode 100644 index 0000000..d8479af --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.Database/Migrations/201605171858497_UserIdAutoGenerate.Designer.cs @@ -0,0 +1,29 @@ +// +namespace RubinChess.Server.Database.Migrations +{ + using System.CodeDom.Compiler; + using System.Data.Entity.Migrations; + using System.Data.Entity.Migrations.Infrastructure; + using System.Resources; + + [GeneratedCode("EntityFramework.Migrations", "6.1.3-40302")] + public sealed partial class UserIdAutoGenerate : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(UserIdAutoGenerate)); + + string IMigrationMetadata.Id + { + get { return "201605171858497_UserIdAutoGenerate"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/RabinChess.Server/RabinChess.Server.Database/Migrations/201605171858497_UserIdAutoGenerate.cs b/RabinChess.Server/RabinChess.Server.Database/Migrations/201605171858497_UserIdAutoGenerate.cs new file mode 100644 index 0000000..60525f6 --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.Database/Migrations/201605171858497_UserIdAutoGenerate.cs @@ -0,0 +1,16 @@ +namespace RubinChess.Server.Database.Migrations +{ + using System; + using System.Data.Entity.Migrations; + + public partial class UserIdAutoGenerate : DbMigration + { + public override void Up() + { + } + + public override void Down() + { + } + } +} diff --git a/RabinChess.Server/RabinChess.Server.Database/Migrations/201605171858497_UserIdAutoGenerate.resx b/RabinChess.Server/RabinChess.Server.Database/Migrations/201605171858497_UserIdAutoGenerate.resx new file mode 100644 index 0000000..d2e4df2 --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.Database/Migrations/201605171858497_UserIdAutoGenerate.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 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 + + + dbo + + \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.Database/Migrations/Configuration.cs b/RabinChess.Server/RabinChess.Server.Database/Migrations/Configuration.cs new file mode 100644 index 0000000..f2dd0e1 --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.Database/Migrations/Configuration.cs @@ -0,0 +1,32 @@ +namespace RubinChess.Server.Database.Migrations +{ + using System; + using System.Data.Entity; + using System.Data.Entity.Migrations; + using System.Linq; + + internal sealed class Configuration : DbMigrationsConfiguration + { + public Configuration() + { + AutomaticMigrationsEnabled = false; + ContextKey = "RubinChess.Server.Database.RubinChessContext"; + } + + protected override void Seed(RubinChess.Server.Database.RubinChessContext context) + { + // This method will be called after migrating to the latest version. + + // You can use the DbSet.AddOrUpdate() helper extension method + // to avoid creating duplicate seed data. E.g. + // + // context.People.AddOrUpdate( + // p => p.FullName, + // new Person { FullName = "Andrew Peters" }, + // new Person { FullName = "Brice Lambson" }, + // new Person { FullName = "Rowan Miller" } + // ); + // + } + } +} diff --git a/RabinChess.Server/RabinChess.Server.Database/RabinChess.Server.Database.csproj b/RabinChess.Server/RabinChess.Server.Database/RabinChess.Server.Database.csproj index 5583d8d..267d09f 100644 --- a/RabinChess.Server/RabinChess.Server.Database/RabinChess.Server.Database.csproj +++ b/RabinChess.Server/RabinChess.Server.Database/RabinChess.Server.Database.csproj @@ -30,7 +30,16 @@ 4 + + ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll + True + + + ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll + True + + @@ -39,7 +48,36 @@ + + + + + + 201605031506553_InitialCreate.cs + + + + 201605171858497_UserIdAutoGenerate.cs + + + + + + + + Designer + PreserveNewest + + + + + + 201605031506553_InitialCreate.cs + + + 201605171858497_UserIdAutoGenerate.cs + +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.Logic.Test/GameRetrieverTests.cs b/RabinChess.Server/RabinChess.Server.Logic.Test/GameRetrieverTests.cs new file mode 100644 index 0000000..78fb8b1 --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.Logic.Test/GameRetrieverTests.cs @@ -0,0 +1,227 @@ +using System; +using System.Collections.Generic; +using System.Data.Entity; +using System.Linq; +using Moq; +using NUnit.Framework; +using RabinChess.Server.DataStructures; +using RubinChess.Server.Database; +using RubinChess.Server.Database.Entities; +using RubinChess.Server.Logic.Interactions; + +namespace RabinChess.Server.Logic.Test +{ + [TestFixture] + public class GameRetrieverTests + { + [Test] + public void ListOfGamesIsRetrieved() + { + var mockGamesSet = TestDataFactory.GetMockGamesSet(); + + var mockDbContext = new Mock(); + mockDbContext.Setup(m => m.Games).Returns(mockGamesSet.Object); + + var gamesRetriever = new GamesRetriever(mockDbContext.Object); + var games = gamesRetriever.GetGames(1); + + Assert.AreEqual(2, games.Count); + Assert.Contains("Test1", games.Select(g => g.Name).ToList()); + Assert.Contains("Test2", games.Select(g => g.Name).ToList()); + Assert.Contains("Tag1 vs. Tag2 | Tag3", games.Select(g => g.Tags).ToList()); + Assert.Contains("Tag4 vs. Tag5 | Tag6", games.Select(g => g.Tags).ToList()); + } + + [Test] + public void SingleGameIsRetrieved() + { + var mockGameSet = TestDataFactory.GetMockGamesSet(); + + var mockDbContext = new Mock(); + mockDbContext.Setup(m => m.Games).Returns(mockGameSet.Object); + + var gamesRetriever = new GamesRetriever(mockDbContext.Object); + var game = gamesRetriever.GetGame(new Guid("11111111-1111-1111-1111-111111111111")); + + Assert.IsNotNull(game); + Assert.AreEqual("Test1", game.Name); + } + + [Test] + public void GameIsDeleted() + { + var mockGameSet = TestDataFactory.GetMockGamesSet(); + + var mockDbContext = new Mock(); + mockDbContext.Setup(m => m.Games).Returns(mockGameSet.Object); + + var gamesRetriever = new GamesRetriever(mockDbContext.Object); + bool result = gamesRetriever.DeleteGame(new Guid("11111111-1111-1111-1111-111111111111")); + + mockGameSet.Verify(m => m.Remove(It.IsAny()), Times.Once); + mockDbContext.Verify(m => m.SaveChanges(), Times.Once); + + Assert.IsTrue(result); + } + + [Test] + [Ignore] + public void GameIsAdded() + { + var game = (GameVM) TestDataFactory.GetSampleGame(); + var mockGameSet = new Mock>(); + + var mockDbContext = new Mock(); + mockDbContext.Setup(m => m.Games).Returns(mockGameSet.Object); + + var gamesRetriever = new GamesRetriever(mockDbContext.Object); + var id = gamesRetriever.AddGame(game); + + mockGameSet.Verify(m => m.Add(It.IsAny()), Times.Once); + mockDbContext.Verify(m => m.SaveChanges(), Times.Once); + + Assert.IsNotNull(id); + } + + [Test] + public void NonExistingGameIsRetrieved() + { + var mockGameSet = TestDataFactory.GetMockGamesSet(); + + var mockDbContext = new Mock(); + mockDbContext.Setup(m => m.Games).Returns(mockGameSet.Object); + + var gamesRetriever = new GamesRetriever(mockDbContext.Object); + Assert.Throws(() => gamesRetriever.GetGame(new Guid("11111111-1111-1231-1111-111111111111"))); + } + + [Test] + public void NonExistingGameIsDeleted() + { + var mockGameSet = TestDataFactory.GetMockGamesSet(); + + var mockDbContext = new Mock(); + mockDbContext.Setup(m => m.Games).Returns(mockGameSet.Object); + + var gamesRetriever = new GamesRetriever(mockDbContext.Object); + bool result = gamesRetriever.DeleteGame(new Guid("11111111-1231-1111-1111-111111111111")); + + Assert.IsFalse(result); + } + + [Test] + public void EmptyListOfGamesIsRetrieved() + { + var mockGamesSet = TestDataFactory.GetMockGamesSet(); + + var mockDbContext = new Mock(); + mockDbContext.Setup(m => m.Games).Returns(mockGamesSet.Object); + + var gamesRetriever = new GamesRetriever(mockDbContext.Object); + var games = gamesRetriever.GetGames(2); + + Assert.AreEqual(0, games.Count); + } + + [Test] + public void TagSubtitleIsCreatedProperly() + { + List tags = new List + { + new GameTag + { + Name = "Elo", + Value = "250" + }, + new GameTag + { + Name = "Black", + Value = "Black" + }, + new GameTag + { + Name = "Event", + Value = "Event" + }, + new GameTag + { + Name = "SomeTag", + Value = "Lawl" + }, + new GameTag + { + Name = "White", + Value = "White" + } + }; + + var tagSub = GamesRetriever.TagsStringCreator(tags); + + Assert.AreEqual("White vs. Black | Event", tagSub); + } + + [Test] + public void TagSubtitleIsCreatedProperlyIfNotAllTagsPresent() + { + List tags = new List + { + new GameTag + { + Name = "Elo", + Value = "250" + }, + new GameTag + { + Name = "Event", + Value = "Event" + }, + new GameTag + { + Name = "SomeTag", + Value = "Lawl" + }, + new GameTag + { + Name = "White", + Value = "White" + } + }; + + var tagSub = GamesRetriever.TagsStringCreator(tags); + + Assert.AreEqual("White vs. ? | Event", tagSub); + } + + [Test] + public void TagSubtitleIsCreatedProperlyIfNoRequiredTagsPresent() + { + List tags = new List + { + new GameTag + { + Name = "Elo", + Value = "250" + }, + new GameTag + { + Name = "SomeTag", + Value = "Lawl" + } + }; + + var tagSub = GamesRetriever.TagsStringCreator(tags); + + Assert.AreEqual("Click \"View\" to see tags!", tagSub); + } + + [Test] + public void TagsSubtitleIsCreatedProperlyIfNoTagsPresent() + { + List tags = new List(); + + var tagSub = GamesRetriever.TagsStringCreator(tags); + + Assert.AreEqual("This game has no tags. Click \"Edit\" to add some!", tagSub); + } + } +} \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.Logic.Test/Properties/AssemblyInfo.cs b/RabinChess.Server/RabinChess.Server.Logic.Test/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..2fdbdc7 --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.Logic.Test/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("RabinChess.Server.Logic.Test")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("RabinChess.Server.Logic.Test")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("284e257e-ed82-43cd-8fac-93719c087fb1")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/RabinChess.Server/RabinChess.Server.Logic.Test/RabinChess.Server.Logic.Test.csproj b/RabinChess.Server/RabinChess.Server.Logic.Test/RabinChess.Server.Logic.Test.csproj new file mode 100644 index 0000000..a20c24d --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.Logic.Test/RabinChess.Server.Logic.Test.csproj @@ -0,0 +1,136 @@ + + + + Debug + AnyCPU + {4459DFFF-8F2D-44D7-971F-BF35FB82A819} + Library + Properties + RabinChess.Server.Logic.Test + RabinChess.Server.Logic.Test + v4.5 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll + True + + + ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll + True + + + ..\packages\Moq.4.2.1510.2205\lib\net40\Moq.dll + True + + + ..\packages\NUnitTestAdapter.2.0.0\lib\nunit.core.dll + False + + + ..\packages\NUnitTestAdapter.2.0.0\lib\nunit.core.interfaces.dll + False + + + ..\packages\NUnit.2.6.4\lib\nunit.framework.dll + True + + + ..\packages\NUnitTestAdapter.2.0.0\lib\nunit.util.dll + False + + + ..\packages\NUnitTestAdapter.2.0.0\lib\NUnit.VisualStudio.TestAdapter.dll + False + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {28073fba-2df2-4df6-a519-e4ddff3cded9} + RabinChess.Server.Database + + + {3d934dc5-2d2b-496c-aa69-17bc7a7d07a5} + RabinChess.Server.DataStructures + + + {07351139-6d20-4748-a41d-6b0cfe0086b2} + RabinChess.Server.Logic + + + + + + + False + + + False + + + False + + + False + + + + + + + + \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.Logic.Test/TestDataFactory.cs b/RabinChess.Server/RabinChess.Server.Logic.Test/TestDataFactory.cs new file mode 100644 index 0000000..08daa0e --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.Logic.Test/TestDataFactory.cs @@ -0,0 +1,214 @@ +using System; +using System.Collections.Generic; +using System.Data.Entity; +using System.Linq; +using Moq; +using RubinChess.Server.Database.Entities; + +namespace RabinChess.Server.Logic.Test +{ + public static class TestDataFactory + { + public static Mock> GetMockGamesSet() + { + var games = GetSampleGames().AsQueryable(); + + var mockGamesSet = new Mock>(); + mockGamesSet.As>().Setup(m => m.Provider).Returns(games.Provider); + mockGamesSet.As>().Setup(m => m.Expression).Returns(games.Expression); + mockGamesSet.As>().Setup(m => m.ElementType).Returns(games.ElementType); + mockGamesSet.As>().Setup(m => m.GetEnumerator()).Returns(() => games.GetEnumerator()); + + return mockGamesSet; + } + + public static Mock> GetMockUsersSet() + { + User user = GetSampleUser(); + user.Games = GetSampleGames(); + user.Id = 1; + + var users = new List + { + user + }.AsQueryable(); + + var mockUserSet = new Mock>(); + mockUserSet.As>().Setup(m => m.Provider).Returns(users.Provider); + mockUserSet.As>().Setup(m => m.Expression).Returns(users.Expression); + mockUserSet.As>().Setup(m => m.ElementType).Returns(users.ElementType); + mockUserSet.As>().Setup(m => m.GetEnumerator()).Returns(() => users.GetEnumerator()); + + return mockUserSet; + } + + public static Mock> GetMockTagSet() + { + var tags = new List + { + new GameTag + { + GameId = new Guid("11111111-1111-1111-1111-111111111111"), + Name = "White", + Value = "Tag1" + }, + new GameTag + { + GameId = new Guid("11111111-1111-1111-1111-111111111111"), + Name = "Black", + Value = "Tag2" + }, + new GameTag + { + GameId = new Guid("11111111-1111-1111-1111-111111111111"), + Name = "Event", + Value = "Tag3" + }, + new GameTag + { + GameId = new Guid("22222222-2222-2222-2222-222222222222"), + Name = "White", + Value = "Tag4" + }, + new GameTag + { + GameId = new Guid("22222222-2222-2222-2222-222222222222"), + Name = "Black", + Value = "Tag5" + }, + new GameTag + { + GameId = new Guid("22222222-2222-2222-2222-222222222222"), + Name = "Event", + Value = "Tag6" + } + }.AsQueryable(); + + var mockTagsSet = new Mock>(); + mockTagsSet.As>().Setup(m => m.Provider).Returns(tags.Provider); + mockTagsSet.As>().Setup(m => m.Expression).Returns(tags.Expression); + mockTagsSet.As>().Setup(m => m.ElementType).Returns(tags.ElementType); + mockTagsSet.As>().Setup(m => m.GetEnumerator()).Returns(() => tags.GetEnumerator()); + + return mockTagsSet; + } + + public static User GetSampleUser() + { + return new User + { + Email = "test@test.com", + FirstName = "Name", + LastName = "Surname", + PasswordHash = "1a1dc91c907325c69271ddf0c944bc72", + UserName = "User123" + }; + } + + public static List GetSampleGames() + { + var games = new List + { + new Game + { + Id = new Guid("11111111-1111-1111-1111-111111111111"), + Name = "Test1", + Tags = + new List + { + new GameTag + { + GameId = new Guid("11111111-1111-1111-1111-111111111111"), + Name = "White", + Value = "Tag1" + }, + new GameTag + { + GameId = new Guid("11111111-1111-1111-1111-111111111111"), + Name = "Black", + Value = "Tag2" + }, + new GameTag + { + GameId = new Guid("11111111-1111-1111-1111-111111111111"), + Name = "Event", + Value = "Tag3" + } + }, + GameNotation = "", + UserId = 1 + }, + new Game + { + Id = new Guid("22222222-2222-2222-2222-222222222222"), + Name = "Test2", + Tags = + new List + { + new GameTag + { + GameId = new Guid("22222222-2222-2222-2222-222222222222"), + Name = "White", + Value = "Tag4" + }, + new GameTag + { + GameId = new Guid("22222222-2222-2222-2222-222222222222"), + Name = "Black", + Value = "Tag5" + }, + new GameTag + { + GameId = new Guid("22222222-2222-2222-2222-222222222222"), + Name = "Event", + Value = "Tag6" + } + }, + GameNotation = "", + UserId = 1 + } + }; + + var user = GetSampleUser(); + user.Id = 1; + user.Games = games; + + games.ForEach(g => g.User = user); + + return games; + } + + public static Game GetSampleGame() + { + return new Game + { + Id = new Guid("11111111-1111-1111-1111-111111111111"), + Name = "Test1", + Tags = + new List + { + new GameTag + { + GameId = new Guid("11111111-1111-1111-1111-111111111111"), + Name = "White", + Value = "Tag1" + }, + new GameTag + { + GameId = new Guid("11111111-1111-1111-1111-111111111111"), + Name = "Black", + Value = "Tag2" + }, + new GameTag + { + GameId = new Guid("11111111-1111-1111-1111-111111111111"), + Name = "Event", + Value = "Tag3" + } + }, + GameNotation = "", + UserId = 1 + }; + } + } +} \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.Logic.Test/UserManagerTests.cs b/RabinChess.Server/RabinChess.Server.Logic.Test/UserManagerTests.cs new file mode 100644 index 0000000..0c563d3 --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.Logic.Test/UserManagerTests.cs @@ -0,0 +1,169 @@ +using System.Collections.Generic; +using System.Data.Entity; +using Moq; +using NUnit.Framework; +using RubinChess.Server.Database; +using RubinChess.Server.Database.Entities; +using RubinChess.Server.Logic.Interactions; + +namespace RabinChess.Server.Logic.Test +{ + [TestFixture] + public class UserManagerTests + { + [Test] + [Ignore] + public void UserIsAdded() + { + var mockUserSet = new Mock>(); + + var mockDbContext = new Mock(); + mockDbContext.Setup(m => m.Users).Returns(mockUserSet.Object); + + var userManager = new UserManager(mockDbContext.Object); + int userId = userManager.AddUser(TestDataFactory.GetSampleUser()); + + mockUserSet.Verify(m => m.Add(It.IsAny()), Times.Once); + mockDbContext.Verify(m => m.SaveChanges(), Times.Once); + + Assert.AreNotEqual(0, userId); + } + + [Test] + public void UserIsRemoved() + { + var mockUserSet = TestDataFactory.GetMockUsersSet(); + + var mockDbContext = new Mock(); + mockDbContext.Setup(m => m.Users).Returns(mockUserSet.Object); + + var userManager = new UserManager(mockDbContext.Object); + bool deleted = userManager.DeleteUser(1); + + mockUserSet.Verify(m => m.Remove(It.IsAny()), Times.Once); + mockDbContext.Verify(m => m.SaveChanges(), Times.Once); + + Assert.IsTrue(deleted); + } + + [Test] + public void UserIsUpdated() + { + var updatedUser = TestDataFactory.GetSampleUser(); + updatedUser.Id = 1; + updatedUser.Games = new List(); + updatedUser.FirstName = "OtherName"; + + var mockUserSet = TestDataFactory.GetMockUsersSet(); + + var mockDbContext = new Mock(); + mockDbContext.Setup(m => m.Users).Returns(mockUserSet.Object); + + var userManager = new UserManager(mockDbContext.Object); + int userId = userManager.UpdateUser(updatedUser); + + mockDbContext.Verify(m => m.SaveChanges(), Times.Once); + + Assert.AreEqual(1, userId); + } + + [Test] + [Ignore] + public void UserIsRetrievedById() + { + var mockUserSet = TestDataFactory.GetMockUsersSet(); + var mockGameSet = TestDataFactory.GetMockGamesSet(); + var mockTagSet = TestDataFactory.GetMockTagSet(); + + var mockDbContext = new Mock(); + mockDbContext.Setup(m => m.Users).Returns(mockUserSet.Object); + mockDbContext.Setup(m => m.Games).Returns(mockGameSet.Object); + mockDbContext.Setup(m => m.GameTags).Returns(mockTagSet.Object); + + var userManager = new UserManager(mockDbContext.Object); + var user = userManager.GetUser(1); + + Assert.AreEqual(1, user.Id); + Assert.AreEqual("Name", user.FirstName); + Assert.AreEqual("Surname", user.LastName); + Assert.AreEqual("User123", user.UserName); + Assert.AreEqual("test@test.com", user.Email); + } + + [Test] + [Ignore] + public void UserIsRetrievedByName() + { + var mockUserSet = TestDataFactory.GetMockUsersSet(); + var mockGameSet = TestDataFactory.GetMockGamesSet(); + var mockTagSet = TestDataFactory.GetMockTagSet(); + + var mockDbContext = new Mock(); + mockDbContext.Setup(m => m.Users).Returns(mockUserSet.Object); + mockDbContext.Setup(m => m.Games).Returns(mockGameSet.Object); + mockDbContext.Setup(m => m.GameTags).Returns(mockTagSet.Object); + + var userManager = new UserManager(mockDbContext.Object); + var user = userManager.GetUser("User123"); + + Assert.AreEqual(1, user.Id); + Assert.AreEqual("Name", user.FirstName); + Assert.AreEqual("Surname", user.LastName); + Assert.AreEqual("User123", user.UserName); + Assert.AreEqual("test@test.com", user.Email); + } + + [Test] + public void GetUserWithNonExistingId() + { + var mockUserSet = TestDataFactory.GetMockUsersSet(); + + var mockDbContext = new Mock(); + mockDbContext.Setup(m => m.Users).Returns(mockUserSet.Object); + + var userManager = new UserManager(mockDbContext.Object); + Assert.Throws(() => userManager.GetUser(8)); + } + + [Test] + public void GetUserWithNonExistingName() + { + var mockUserSet = TestDataFactory.GetMockUsersSet(); + + var mockDbContext = new Mock(); + mockDbContext.Setup(m => m.Users).Returns(mockUserSet.Object); + + var userManager = new UserManager(mockDbContext.Object); + Assert.Throws(() => userManager.GetUser("Hahahah")); + } + + [Test] + public void DeleteNonExistingUser() + { + var mockUserSet = TestDataFactory.GetMockUsersSet(); + + var mockDbContext = new Mock(); + mockDbContext.Setup(m => m.Users).Returns(mockUserSet.Object); + + var userManager = new UserManager(mockDbContext.Object); + bool result = userManager.DeleteUser(8); + + Assert.IsFalse(result); + } + + [Test] + public void UpdateNonExistingUser() + { + var userToUpdate = TestDataFactory.GetSampleUser(); + userToUpdate.Id = 17; + + var mockUserSet = TestDataFactory.GetMockUsersSet(); + + var mockDbContext = new Mock(); + mockDbContext.Setup(m => m.Users).Returns(mockUserSet.Object); + + var userManager = new UserManager(mockDbContext.Object); + Assert.Throws(() => userManager.UpdateUser(userToUpdate)); + } + } +} \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.Logic.Test/packages.config b/RabinChess.Server/RabinChess.Server.Logic.Test/packages.config new file mode 100644 index 0000000..a40006f --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.Logic.Test/packages.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.Logic/App.config b/RabinChess.Server/RabinChess.Server.Logic/App.config new file mode 100644 index 0000000..7e1d79c --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.Logic/App.config @@ -0,0 +1,17 @@ + + + + +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.Logic/ContextFactory.cs b/RabinChess.Server/RabinChess.Server.Logic/ContextFactory.cs new file mode 100644 index 0000000..8f44ce2 --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.Logic/ContextFactory.cs @@ -0,0 +1,30 @@ +using RubinChess.Server.Database; +using RubinChess.Server.Logic.Contexts; +using RubinChess.Server.Logic.Interactions; + +namespace RubinChess.Server.Logic +{ + public static class ContextFactory + { + private static IUserContext UserContext { get; set; } + + private static IGamesContext GamesContext { get; set; } + + static ContextFactory() + { + var context = new RubinChessContext(); + GamesContext = new GamesContext(new GamesRetriever(context)); + UserContext = new UserContext(new UserManager(context)); + } + + public static IGamesContext GetGamesContext() + { + return GamesContext; + } + + public static IUserContext GetUserContext() + { + return UserContext; + } + } +} \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.Logic/Contexts/GamesContext.cs b/RabinChess.Server/RabinChess.Server.Logic/Contexts/GamesContext.cs new file mode 100644 index 0000000..827eb5e --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.Logic/Contexts/GamesContext.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using RabinChess.Server.DataStructures; +using RubinChess.Server.Logic.Interactions; + +namespace RubinChess.Server.Logic.Contexts +{ + public class GamesContext : IGamesContext + { + private IGamesRetriever GamesRetriever { get; set; } + + public GamesContext(IGamesRetriever gamesRetriever) + { + GamesRetriever = gamesRetriever; + } + + public List GetGames(int userId) + { + return GamesRetriever.GetGames(userId); + } + } +} \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.Logic/Contexts/IGamesContext.cs b/RabinChess.Server/RabinChess.Server.Logic/Contexts/IGamesContext.cs new file mode 100644 index 0000000..7e96e52 --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.Logic/Contexts/IGamesContext.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using RabinChess.Server.DataStructures; + +namespace RubinChess.Server.Logic.Contexts +{ + public interface IGamesContext + { + List GetGames(int userId); + } +} \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.Logic/Contexts/IUserContext.cs b/RabinChess.Server/RabinChess.Server.Logic/Contexts/IUserContext.cs new file mode 100644 index 0000000..3d88450 --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.Logic/Contexts/IUserContext.cs @@ -0,0 +1,13 @@ +using RubinChess.Server.Database.Entities; + +namespace RubinChess.Server.Logic.Contexts +{ + public interface IUserContext + { + int AddUser(User user); + bool DeleteUser(int userId); + int UpdateUser(User user); + User GetUser(int userId); + User GetUser(string userName); + } +} \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.Logic/Contexts/UserContext.cs b/RabinChess.Server/RabinChess.Server.Logic/Contexts/UserContext.cs new file mode 100644 index 0000000..ebfcbf9 --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.Logic/Contexts/UserContext.cs @@ -0,0 +1,40 @@ +using RubinChess.Server.Database.Entities; +using RubinChess.Server.Logic.Interactions; + +namespace RubinChess.Server.Logic.Contexts +{ + public class UserContext : IUserContext + { + private IUserManager UserManager { get; set; } + + public UserContext(IUserManager userManager) + { + UserManager = userManager; + } + + public int AddUser(User user) + { + return UserManager.AddUser(user); + } + + public bool DeleteUser(int userId) + { + return UserManager.DeleteUser(userId); + } + + public int UpdateUser(User user) + { + return UserManager.UpdateUser(user); + } + + public User GetUser(int userId) + { + return UserManager.GetUser(userId); + } + + public User GetUser(string userName) + { + return UserManager.GetUser(userName); + } + } +} \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.Logic/Interactions/GamesRetriever.cs b/RabinChess.Server/RabinChess.Server.Logic/Interactions/GamesRetriever.cs new file mode 100644 index 0000000..1b42adc --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.Logic/Interactions/GamesRetriever.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.Design; +using System.Linq; +using RabinChess.Server.DataStructures; +using RubinChess.Server.Database; +using RubinChess.Server.Database.Entities; + +namespace RubinChess.Server.Logic.Interactions +{ + public class GamesRetriever : IGamesRetriever + { + private RubinChessContext _context; + + public GamesRetriever(RubinChessContext context) + { + _context = context; + } + + public List GetGames(int userId) + { + var gameListItems = new List(); + List games = _context.Games.Where(g => g.UserId == userId).ToList(); + games.ForEach(game => gameListItems.Add(new GameListItemVM{Id = game.Id, Name = game.Name, Tags = TagsStringCreator(game.Tags)})); + return gameListItems; + } + + public GameVM GetGame(Guid gameId) + { + return (GameVM) _context.Games.FirstOrDefault(g => g.Id == gameId); + } + + public Guid AddGame(GameVM game) + { + var gameEntity = (Game) game; + Game res = _context.Games.Add(gameEntity); + _context.SaveChanges(); + return res.Id; + } + + public bool DeleteGame(Guid gameId) + { + var game = _context.Games.FirstOrDefault(g => g.Id == gameId); + + if (game == null) + return false; + + _context.Games.Remove(game); + _context.SaveChanges(); + + return true; + } + + public static string TagsStringCreator(List tags) + { + string tagsString = string.Empty; + + if (tags.Count == 0) + return "This game has no tags. Click \"Edit\" to add some!"; + + var white = tags.FirstOrDefault(tag => tag.Name == "White"); + var black = tags.FirstOrDefault(tag => tag.Name == "Black"); + var ev = tags.FirstOrDefault(tag => tag.Name == "Event"); + + if (white == null && black == null && ev == null) + return "Click \"View\" to see tags!"; + + tagsString += white != null ? white.Value : "?"; + tagsString += " vs. "; + tagsString += black != null ? black.Value : "?"; + tagsString += " | "; + tagsString += ev != null ? ev.Value : "?"; + + return tagsString; + } + } +} \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.Logic/Interactions/IGamesRetriever.cs b/RabinChess.Server/RabinChess.Server.Logic/Interactions/IGamesRetriever.cs new file mode 100644 index 0000000..c0eea9a --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.Logic/Interactions/IGamesRetriever.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using RabinChess.Server.DataStructures; + +namespace RubinChess.Server.Logic.Interactions +{ + public interface IGamesRetriever + { + List GetGames(int userId); + GameVM GetGame(Guid gameId); + Guid AddGame(GameVM game); + bool DeleteGame(Guid gameId); + } +} \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.Logic/Interactions/IUserManager.cs b/RabinChess.Server/RabinChess.Server.Logic/Interactions/IUserManager.cs new file mode 100644 index 0000000..9ddc284 --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.Logic/Interactions/IUserManager.cs @@ -0,0 +1,13 @@ +using RubinChess.Server.Database.Entities; + +namespace RubinChess.Server.Logic.Interactions +{ + public interface IUserManager + { + int AddUser(User user); + bool DeleteUser(int userId); + int UpdateUser(User user); + User GetUser(int userId); + User GetUser(string userName); + } +} \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.Logic/Interactions/UserManager.cs b/RabinChess.Server/RabinChess.Server.Logic/Interactions/UserManager.cs new file mode 100644 index 0000000..698f613 --- /dev/null +++ b/RabinChess.Server/RabinChess.Server.Logic/Interactions/UserManager.cs @@ -0,0 +1,62 @@ +using System.Data.Entity; +using System.Linq; +using RubinChess.Server.Database; +using RubinChess.Server.Database.Entities; + +namespace RubinChess.Server.Logic.Interactions +{ + public class UserManager : IUserManager + { + private RubinChessContext _context; + + public UserManager(RubinChessContext context) + { + _context = context; + } + + public int AddUser(User user) + { + User newUser = _context.Users.Add(user); + _context.SaveChanges(); + int newUserId = newUser.Id; + + return newUserId; + } + + public bool DeleteUser(int userId) + { + var userToDelete = _context.Users.FirstOrDefault(user => user.Id == userId); + if (userToDelete == null) + return false; + _context.Users.Remove(userToDelete); + _context.SaveChanges(); + + return true; + } + + public int UpdateUser(User user) + { + var existingUser = _context.Users.FirstOrDefault(dbUser => dbUser.Id == user.Id); + existingUser.Email = user.Email; + existingUser.FirstName = user.FirstName; + existingUser.LastName = user.LastName; + existingUser.PasswordHash = user.PasswordHash; + existingUser.UserName = user.UserName; + _context.SaveChanges(); + int userId = existingUser.Id; + return userId; + } + + public User GetUser(int userId) + { + User user = _context.Users.Include(dbUser => dbUser.Games).FirstOrDefault(dbUser => dbUser.Id == userId); + return user; + } + + public User GetUser(string userName) + { + User user = _context.Users.Include(dbUser => dbUser.Games).FirstOrDefault(dbUser => dbUser.UserName == userName); + return user; + } + } +} \ No newline at end of file diff --git a/RabinChess.Server/RabinChess.Server.Logic/RabinChess.Server.Logic.csproj b/RabinChess.Server/RabinChess.Server.Logic/RabinChess.Server.Logic.csproj index 31b61fa..820c3d8 100644 --- a/RabinChess.Server/RabinChess.Server.Logic/RabinChess.Server.Logic.csproj +++ b/RabinChess.Server/RabinChess.Server.Logic/RabinChess.Server.Logic.csproj @@ -30,7 +30,16 @@ 4 + + ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll + True + + + ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll + True + + @@ -39,6 +48,15 @@ + + + + + + + + + @@ -46,7 +64,16 @@ {28073fba-2df2-4df6-a519-e4ddff3cded9} RabinChess.Server.Database + + {3d934dc5-2d2b-496c-aa69-17bc7a7d07a5} + RabinChess.Server.DataStructures + + + + + +