From 991097235bcc86169e69b6e59fd43714efc7ba18 Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Fri, 16 Sep 2016 13:58:36 +0200 Subject: [PATCH 01/53] update nuget packages to current releases --- .../BoardGame.Api/BoardGame.Api.csproj | 28 ++++------ .../SwaggerUi/CustomAssets/lang/en.js | 53 ------------------- .../SwaggerUi/CustomAssets/lang/es.js | 52 ------------------ .../SwaggerUi/CustomAssets/lang/pt.js | 53 ------------------- .../SwaggerUi/CustomAssets/lang/ru.js | 52 ------------------ .../SwaggerUi/CustomAssets/lang/tr.js | 53 ------------------- .../SwaggerUi/CustomAssets/lang/translator.js | 39 -------------- src/BoardZApi/BoardGame.Api/app.config | 6 ++- src/BoardZApi/BoardGame.Api/packages.config | 12 ++--- src/BoardZApi/BoardGame.Host/App.config | 6 ++- .../BoardGame.Host/BoardGame.Host.csproj | 20 +++---- .../SwaggerUi/CustomAssets/lang/en.js | 53 ------------------- .../SwaggerUi/CustomAssets/lang/es.js | 52 ------------------ .../SwaggerUi/CustomAssets/lang/pt.js | 53 ------------------- .../SwaggerUi/CustomAssets/lang/ru.js | 52 ------------------ .../SwaggerUi/CustomAssets/lang/tr.js | 53 ------------------- .../SwaggerUi/CustomAssets/lang/translator.js | 39 -------------- src/BoardZApi/BoardGame.Host/packages.config | 8 +-- .../BoardGame.WebHost.csproj | 35 +++++------- .../SwaggerUi/CustomAssets/lang/en.js | 53 ------------------- .../SwaggerUi/CustomAssets/lang/es.js | 52 ------------------ .../SwaggerUi/CustomAssets/lang/pt.js | 53 ------------------- .../SwaggerUi/CustomAssets/lang/ru.js | 52 ------------------ .../SwaggerUi/CustomAssets/lang/tr.js | 53 ------------------- .../SwaggerUi/CustomAssets/lang/translator.js | 39 -------------- .../BoardGame.WebHost/packages.config | 14 ++--- src/BoardZApi/BoardGame.WebHost/web.config | 19 ++++--- 27 files changed, 68 insertions(+), 986 deletions(-) delete mode 100644 src/BoardZApi/BoardGame.Api/SwaggerUi/CustomAssets/lang/en.js delete mode 100644 src/BoardZApi/BoardGame.Api/SwaggerUi/CustomAssets/lang/es.js delete mode 100644 src/BoardZApi/BoardGame.Api/SwaggerUi/CustomAssets/lang/pt.js delete mode 100644 src/BoardZApi/BoardGame.Api/SwaggerUi/CustomAssets/lang/ru.js delete mode 100644 src/BoardZApi/BoardGame.Api/SwaggerUi/CustomAssets/lang/tr.js delete mode 100644 src/BoardZApi/BoardGame.Api/SwaggerUi/CustomAssets/lang/translator.js delete mode 100644 src/BoardZApi/BoardGame.Host/SwaggerUi/CustomAssets/lang/en.js delete mode 100644 src/BoardZApi/BoardGame.Host/SwaggerUi/CustomAssets/lang/es.js delete mode 100644 src/BoardZApi/BoardGame.Host/SwaggerUi/CustomAssets/lang/pt.js delete mode 100644 src/BoardZApi/BoardGame.Host/SwaggerUi/CustomAssets/lang/ru.js delete mode 100644 src/BoardZApi/BoardGame.Host/SwaggerUi/CustomAssets/lang/tr.js delete mode 100644 src/BoardZApi/BoardGame.Host/SwaggerUi/CustomAssets/lang/translator.js delete mode 100644 src/BoardZApi/BoardGame.WebHost/SwaggerUi/CustomAssets/lang/en.js delete mode 100644 src/BoardZApi/BoardGame.WebHost/SwaggerUi/CustomAssets/lang/es.js delete mode 100644 src/BoardZApi/BoardGame.WebHost/SwaggerUi/CustomAssets/lang/pt.js delete mode 100644 src/BoardZApi/BoardGame.WebHost/SwaggerUi/CustomAssets/lang/ru.js delete mode 100644 src/BoardZApi/BoardGame.WebHost/SwaggerUi/CustomAssets/lang/tr.js delete mode 100644 src/BoardZApi/BoardGame.WebHost/SwaggerUi/CustomAssets/lang/translator.js diff --git a/src/BoardZApi/BoardGame.Api/BoardGame.Api.csproj b/src/BoardZApi/BoardGame.Api/BoardGame.Api.csproj index 0ae60ba..501a5fc 100644 --- a/src/BoardZApi/BoardGame.Api/BoardGame.Api.csproj +++ b/src/BoardZApi/BoardGame.Api/BoardGame.Api.csproj @@ -32,16 +32,16 @@ ..\docs\BoardGame.Api.xml - - ..\packages\Autofac.3.5.2\lib\net40\Autofac.dll + + ..\packages\Autofac.4.1.0\lib\net45\Autofac.dll True - - ..\packages\Autofac.WebApi2.3.4.0\lib\net45\Autofac.Integration.WebApi.dll + + ..\packages\Autofac.WebApi2.4.0.0\lib\net45\Autofac.Integration.WebApi.dll True - - ..\packages\Microsoft.AspNet.SignalR.Core.2.2.0\lib\net45\Microsoft.AspNet.SignalR.Core.dll + + ..\packages\Microsoft.AspNet.SignalR.Core.2.2.1\lib\net45\Microsoft.AspNet.SignalR.Core.dll True @@ -64,8 +64,8 @@ ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll True - - ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll + + ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll True @@ -73,7 +73,7 @@ True - ..\packages\Swashbuckle.Blue.Core.5.15\lib\net451\Swashbuckle.Core.dll + ..\packages\Swashbuckle.Blue.Core.5.54.0\lib\net451\Swashbuckle.Core.dll True @@ -110,7 +110,7 @@ - ..\packages\WebActivatorEx.2.0\lib\net40\WebActivatorEx.dll + ..\packages\WebActivatorEx.2.1.0\lib\net40\WebActivatorEx.dll True @@ -139,14 +139,6 @@ - - - - - - - - Models.tst diff --git a/src/BoardZApi/BoardGame.Api/SwaggerUi/CustomAssets/lang/en.js b/src/BoardZApi/BoardGame.Api/SwaggerUi/CustomAssets/lang/en.js deleted file mode 100644 index 776a8b7..0000000 --- a/src/BoardZApi/BoardGame.Api/SwaggerUi/CustomAssets/lang/en.js +++ /dev/null @@ -1,53 +0,0 @@ -'use strict'; - -/* jshint quotmark: double */ -window.SwaggerTranslator.learn({ - "Warning: Deprecated":"Warning: Deprecated", - "Implementation Notes":"Implementation Notes", - "Response Class":"Response Class", - "Status":"Status", - "Parameters":"Parameters", - "Parameter":"Parameter", - "Value":"Value", - "Description":"Description", - "Parameter Type":"Parameter Type", - "Data Type":"Data Type", - "Response Messages":"Response Messages", - "HTTP Status Code":"HTTP Status Code", - "Reason":"Reason", - "Response Model":"Response Model", - "Request URL":"Request URL", - "Response Body":"Response Body", - "Response Code":"Response Code", - "Response Headers":"Response Headers", - "Hide Response":"Hide Response", - "Headers":"Headers", - "Try it out!":"Try it out!", - "Show/Hide":"Show/Hide", - "List Operations":"List Operations", - "Expand Operations":"Expand Operations", - "Raw":"Raw", - "can't parse JSON. Raw result":"can't parse JSON. Raw result", - "Model Schema":"Model Schema", - "Model":"Model", - "apply":"apply", - "Username":"Username", - "Password":"Password", - "Terms of service":"Terms of service", - "Created by":"Created by", - "See more at":"See more at", - "Contact the developer":"Contact the developer", - "api version":"api version", - "Response Content Type":"Response Content Type", - "fetching resource":"fetching resource", - "fetching resource list":"fetching resource list", - "Explore":"Explore", - "Show Swagger Petstore Example Apis":"Show Swagger Petstore Example Apis", - "Can't read from server. It may not have the appropriate access-control-origin settings.":"Can't read from server. It may not have the appropriate access-control-origin settings.", - "Please specify the protocol for":"Please specify the protocol for", - "Can't read swagger JSON from":"Can't read swagger JSON from", - "Finished Loading Resource Information. Rendering Swagger UI":"Finished Loading Resource Information. Rendering Swagger UI", - "Unable to read api":"Unable to read api", - "from path":"from path", - "server returned":"server returned" -}); diff --git a/src/BoardZApi/BoardGame.Api/SwaggerUi/CustomAssets/lang/es.js b/src/BoardZApi/BoardGame.Api/SwaggerUi/CustomAssets/lang/es.js deleted file mode 100644 index a8dff60..0000000 --- a/src/BoardZApi/BoardGame.Api/SwaggerUi/CustomAssets/lang/es.js +++ /dev/null @@ -1,52 +0,0 @@ -'use strict'; - -/* jshint quotmark: double */ -window.SwaggerTranslator.learn({ - "Warning: Deprecated":"Advertencia: Obsoleto", - "Implementation Notes":"Notas de implementación", - "Response Class":"Clase de la Respuesta", - "Status":"Status", - "Parameters":"Parámetros", - "Parameter":"Parámetro", - "Value":"Valor", - "Description":"Descripción", - "Parameter Type":"Tipo del Parámetro", - "Data Type":"Tipo del Dato", - "Response Messages":"Mensajes de la Respuesta", - "HTTP Status Code":"Código de Status HTTP", - "Reason":"Razón", - "Response Model":"Modelo de la Respuesta", - "Request URL":"URL de la Solicitud", - "Response Body":"Cuerpo de la Respuesta", - "Response Code":"Código de la Respuesta", - "Response Headers":"Encabezados de la Respuesta", - "Hide Response":"Ocultar Respuesta", - "Try it out!":"Pruébalo!", - "Show/Hide":"Mostrar/Ocultar", - "List Operations":"Listar Operaciones", - "Expand Operations":"Expandir Operaciones", - "Raw":"Crudo", - "can't parse JSON. Raw result":"no puede parsear el JSON. Resultado crudo", - "Model Schema":"Esquema del Modelo", - "Model":"Modelo", - "apply":"aplicar", - "Username":"Nombre de usuario", - "Password":"Contraseña", - "Terms of service":"Términos de Servicio", - "Created by":"Creado por", - "See more at":"Ver más en", - "Contact the developer":"Contactar al desarrollador", - "api version":"versión de la api", - "Response Content Type":"Tipo de Contenido (Content Type) de la Respuesta", - "fetching resource":"buscando recurso", - "fetching resource list":"buscando lista del recurso", - "Explore":"Explorar", - "Show Swagger Petstore Example Apis":"Mostrar Api Ejemplo de Swagger Petstore", - "Can't read from server. It may not have the appropriate access-control-origin settings.":"No se puede leer del servidor. Tal vez no tiene la configuración de control de acceso de origen (access-control-origin) apropiado.", - "Please specify the protocol for":"Por favor, especificar el protocola para", - "Can't read swagger JSON from":"No se puede leer el JSON de swagger desde", - "Finished Loading Resource Information. Rendering Swagger UI":"Finalizada la carga del recurso de Información. Mostrando Swagger UI", - "Unable to read api":"No se puede leer la api", - "from path":"desde ruta", - "server returned":"el servidor retornó" -}); diff --git a/src/BoardZApi/BoardGame.Api/SwaggerUi/CustomAssets/lang/pt.js b/src/BoardZApi/BoardGame.Api/SwaggerUi/CustomAssets/lang/pt.js deleted file mode 100644 index f2e7c13..0000000 --- a/src/BoardZApi/BoardGame.Api/SwaggerUi/CustomAssets/lang/pt.js +++ /dev/null @@ -1,53 +0,0 @@ -'use strict'; - -/* jshint quotmark: double */ -window.SwaggerTranslator.learn({ - "Warning: Deprecated":"Aviso: Depreciado", - "Implementation Notes":"Notas de Implementação", - "Response Class":"Classe de resposta", - "Status":"Status", - "Parameters":"Parâmetros", - "Parameter":"Parâmetro", - "Value":"Valor", - "Description":"Descrição", - "Parameter Type":"Tipo de parâmetro", - "Data Type":"Tipo de dados", - "Response Messages":"Mensagens de resposta", - "HTTP Status Code":"Código de status HTTP", - "Reason":"Razão", - "Response Model":"Modelo resposta", - "Request URL":"URL requisição", - "Response Body":"Corpo da resposta", - "Response Code":"Código da resposta", - "Response Headers":"Cabeçalho da resposta", - "Headers":"Cabeçalhos", - "Hide Response":"Esconder resposta", - "Try it out!":"Tente agora!", - "Show/Hide":"Mostrar/Esconder", - "List Operations":"Listar operações", - "Expand Operations":"Expandir operações", - "Raw":"Cru", - "can't parse JSON. Raw result":"Falha ao analisar JSON. Resulto cru", - "Model Schema":"Modelo esquema", - "Model":"Modelo", - "apply":"Aplicar", - "Username":"Usuário", - "Password":"Senha", - "Terms of service":"Termos do serviço", - "Created by":"Criado por", - "See more at":"Veja mais em", - "Contact the developer":"Contate o desenvolvedor", - "api version":"Versão api", - "Response Content Type":"Tipo de conteúdo da resposta", - "fetching resource":"busca recurso", - "fetching resource list":"buscando lista de recursos", - "Explore":"Explorar", - "Show Swagger Petstore Example Apis":"Show Swagger Petstore Example Apis", - "Can't read from server. It may not have the appropriate access-control-origin settings.":"Não é possível ler do servidor. Pode não ter as apropriadas configurações access-control-origin", - "Please specify the protocol for":"Por favor especifique o protocolo", - "Can't read swagger JSON from":"Não é possível ler o JSON Swagger de", - "Finished Loading Resource Information. Rendering Swagger UI":"Carregar informação de recurso finalizada. Renderizando Swagger UI", - "Unable to read api":"Não foi possível ler api", - "from path":"do caminho", - "server returned":"servidor retornou" -}); diff --git a/src/BoardZApi/BoardGame.Api/SwaggerUi/CustomAssets/lang/ru.js b/src/BoardZApi/BoardGame.Api/SwaggerUi/CustomAssets/lang/ru.js deleted file mode 100644 index 065100f..0000000 --- a/src/BoardZApi/BoardGame.Api/SwaggerUi/CustomAssets/lang/ru.js +++ /dev/null @@ -1,52 +0,0 @@ -'use strict'; - -/* jshint quotmark: double */ -window.SwaggerTranslator.learn({ - "Warning: Deprecated":"Ворнинг: Депрекейтед", - "Implementation Notes":"Заметки", - "Response Class":"Пример ответа", - "Status":"Статус", - "Parameters":"Параметры", - "Parameter":"Параметр", - "Value":"Значение", - "Description":"Описание", - "Parameter Type":"Тип параметра", - "Data Type":"Тип данных", - "HTTP Status Code":"HTTP код", - "Reason":"Причина", - "Response Model":"Структура ответа", - "Request URL":"URL запроса", - "Response Body":"Тело ответа", - "Response Code":"HTTP код ответа", - "Response Headers":"Заголовки ответа", - "Hide Response":"Спрятать ответ", - "Response Messages":"Что может прийти в ответ", - "Try it out!":"Попробовать!", - "Show/Hide":"Показать/Скрыть", - "List Operations":"Операции кратко", - "Expand Operations":"Операции подробно", - "Raw":"В сыром виде", - "can't parse JSON. Raw result":"Не удается распарсить ответ:", - "Model Schema":"Структура", - "Model":"Описание", - "apply":"применить", - "Username":"Имя пользователя", - "Password":"Пароль", - "Terms of service":"Условия использования", - "Created by":"Разработано", - "See more at":"Еще тут", - "Contact the developer":"Связаться с разработчиком", - "api version":"Версия API", - "Response Content Type":"Content Type ответа", - "fetching resource":"Получение ресурса", - "fetching resource list":"Получение ресурсов", - "Explore":"Поехали", - "Show Swagger Petstore Example Apis":"Показать примеры АПИ", - "Can't read from server. It may not have the appropriate access-control-origin settings.":"Не удается получить ответ от сервера. Возможно, какая-то лажа с настройками доступа", - "Please specify the protocol for":"Пожалуйста, укажите протогол для", - "Can't read swagger JSON from":"Не получается прочитать swagger json из", - "Finished Loading Resource Information. Rendering Swagger UI":"Загрузка информации о ресурсах завершена. Рендерим", - "Unable to read api":"Не удалось прочитать api", - "from path":"по адресу", - "server returned":"сервер сказал" -}); diff --git a/src/BoardZApi/BoardGame.Api/SwaggerUi/CustomAssets/lang/tr.js b/src/BoardZApi/BoardGame.Api/SwaggerUi/CustomAssets/lang/tr.js deleted file mode 100644 index 16426a9..0000000 --- a/src/BoardZApi/BoardGame.Api/SwaggerUi/CustomAssets/lang/tr.js +++ /dev/null @@ -1,53 +0,0 @@ -'use strict'; - -/* jshint quotmark: double */ -window.SwaggerTranslator.learn({ - "Warning: Deprecated":"Uyarı: Deprecated", - "Implementation Notes":"Gerçekleştirim Notları", - "Response Class":"Dönen Sınıf", - "Status":"Statü", - "Parameters":"Parametreler", - "Parameter":"Parametre", - "Value":"Değer", - "Description":"Açıklama", - "Parameter Type":"Parametre Tipi", - "Data Type":"Veri Tipi", - "Response Messages":"Dönüş Mesajı", - "HTTP Status Code":"HTTP Statü Kodu", - "Reason":"Gerekçe", - "Response Model":"Dönüş Modeli", - "Request URL":"İstek URL", - "Response Body":"Dönüş İçeriği", - "Response Code":"Dönüş Kodu", - "Response Headers":"Dönüş Üst Bilgileri", - "Hide Response":"Dönüşü Gizle", - "Headers":"Üst Bilgiler", - "Try it out!":"Dene!", - "Show/Hide":"Göster/Gizle", - "List Operations":"Operasyonları Listele", - "Expand Operations":"Operasyonları Aç", - "Raw":"Ham", - "can't parse JSON. Raw result":"JSON çözümlenemiyor. Ham sonuç", - "Model Schema":"Model Şema", - "Model":"Model", - "apply":"uygula", - "Username":"Kullanıcı Adı", - "Password":"Parola", - "Terms of service":"Servis şartları", - "Created by":"Oluşturan", - "See more at":"Daha fazlası için", - "Contact the developer":"Geliştirici ile İletişime Geçin", - "api version":"api versiyon", - "Response Content Type":"Dönüş İçerik Tipi", - "fetching resource":"kaynak getiriliyor", - "fetching resource list":"kaynak listesi getiriliyor", - "Explore":"Keşfet", - "Show Swagger Petstore Example Apis":"Swagger Petstore Örnek Api'yi Gör", - "Can't read from server. It may not have the appropriate access-control-origin settings.":"Sunucudan okuma yapılamıyor. Sunucu access-control-origin ayarlarınızı kontrol edin.", - "Please specify the protocol for":"Lütfen istenen adres için protokol belirtiniz", - "Can't read swagger JSON from":"Swagger JSON bu kaynaktan okunamıyor", - "Finished Loading Resource Information. Rendering Swagger UI":"Kaynak baglantısı tamamlandı. Swagger UI gösterime hazırlanıyor", - "Unable to read api":"api okunamadı", - "from path":"yoldan", - "server returned":"sunucuya dönüldü" -}); diff --git a/src/BoardZApi/BoardGame.Api/SwaggerUi/CustomAssets/lang/translator.js b/src/BoardZApi/BoardGame.Api/SwaggerUi/CustomAssets/lang/translator.js deleted file mode 100644 index 591f6d4..0000000 --- a/src/BoardZApi/BoardGame.Api/SwaggerUi/CustomAssets/lang/translator.js +++ /dev/null @@ -1,39 +0,0 @@ -'use strict'; - -/** - * Translator for documentation pages. - * - * To enable translation you should include one of language-files in your index.html - * after . - * For example - - * - * If you wish to translate some new texsts you should do two things: - * 1. Add a new phrase pair ("New Phrase": "New Translation") into your language file (for example lang/ru.js). It will be great if you add it in other language files too. - * 2. Mark that text it templates this way New Phrase or . - * The main thing here is attribute data-sw-translate. Only inner html, title-attribute and value-attribute are going to translate. - * - */ -window.SwaggerTranslator = { - - _words:[], - - translate: function(sel) { - var $this = this; - sel = sel || '[data-sw-translate]'; - - $(sel).each(function() { - $(this).html($this._tryTranslate($(this).html())); - - $(this).val($this._tryTranslate($(this).val())); - $(this).attr('title', $this._tryTranslate($(this).attr('title'))); - }); - }, - - _tryTranslate: function(word) { - return this._words[$.trim(word)] !== undefined ? this._words[$.trim(word)] : word; - }, - - learn: function(wordsMap) { - this._words = wordsMap; - } -}; diff --git a/src/BoardZApi/BoardGame.Api/app.config b/src/BoardZApi/BoardGame.Api/app.config index bc20ce0..d12ab75 100644 --- a/src/BoardZApi/BoardGame.Api/app.config +++ b/src/BoardZApi/BoardGame.Api/app.config @@ -16,7 +16,7 @@ - + @@ -26,6 +26,10 @@ + + + + \ No newline at end of file diff --git a/src/BoardZApi/BoardGame.Api/packages.config b/src/BoardZApi/BoardGame.Api/packages.config index b359a0a..44a8fc6 100644 --- a/src/BoardZApi/BoardGame.Api/packages.config +++ b/src/BoardZApi/BoardGame.Api/packages.config @@ -1,9 +1,9 @@  - - + + - + @@ -15,8 +15,8 @@ - + - - + + \ No newline at end of file diff --git a/src/BoardZApi/BoardGame.Host/App.config b/src/BoardZApi/BoardGame.Host/App.config index 632fd7e..6d8f92e 100644 --- a/src/BoardZApi/BoardGame.Host/App.config +++ b/src/BoardZApi/BoardGame.Host/App.config @@ -19,7 +19,7 @@ - + @@ -29,6 +29,10 @@ + + + + \ No newline at end of file diff --git a/src/BoardZApi/BoardGame.Host/BoardGame.Host.csproj b/src/BoardZApi/BoardGame.Host/BoardGame.Host.csproj index 13c12af..f6b00c4 100644 --- a/src/BoardZApi/BoardGame.Host/BoardGame.Host.csproj +++ b/src/BoardZApi/BoardGame.Host/BoardGame.Host.csproj @@ -52,12 +52,12 @@ ..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll True - - ..\packages\Microsoft.Owin.Host.HttpListener.2.0.2\lib\net45\Microsoft.Owin.Host.HttpListener.dll + + ..\packages\Microsoft.Owin.Host.HttpListener.3.0.1\lib\net45\Microsoft.Owin.Host.HttpListener.dll True - - ..\packages\Microsoft.Owin.Hosting.2.0.2\lib\net45\Microsoft.Owin.Hosting.dll + + ..\packages\Microsoft.Owin.Hosting.3.0.1\lib\net45\Microsoft.Owin.Hosting.dll True @@ -68,8 +68,8 @@ ..\packages\Microsoft.Owin.Security.OAuth.3.0.1\lib\net45\Microsoft.Owin.Security.OAuth.dll True - - ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll + + ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll True @@ -77,7 +77,7 @@ True - ..\packages\Swashbuckle.Blue.Core.5.15\lib\net451\Swashbuckle.Core.dll + ..\packages\Swashbuckle.Blue.Core.5.54.0\lib\net451\Swashbuckle.Core.dll True @@ -136,12 +136,6 @@ App_Data\BoardGame.Api.xml Always - - - - - - +
+ @@ -32,4 +36,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/BoardZApi/BoardGame.Api/packages.config b/src/BoardZApi/BoardGame.Api/packages.config index 44a8fc6..42b5ea5 100644 --- a/src/BoardZApi/BoardGame.Api/packages.config +++ b/src/BoardZApi/BoardGame.Api/packages.config @@ -2,6 +2,7 @@ + From e6a049b25ccb52df22175c07f0d68cb33bcf1fe4 Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Sat, 17 Sep 2016 16:26:04 +0200 Subject: [PATCH 15/53] Service Layer implemented for new BoardZ datamodel --- .../Services/AgeRatingService.cs | 30 ++++ .../Services/CategoriesService.cs | 132 +++++++++++++++++ .../BoardGame.Api/Services/GameService.cs | 133 +++++++++++++++++ .../BoardGame.Api/Services/PlayerService.cs | 139 ++++++++++++++++++ 4 files changed, 434 insertions(+) create mode 100644 src/BoardZApi/BoardGame.Api/Services/AgeRatingService.cs create mode 100644 src/BoardZApi/BoardGame.Api/Services/CategoriesService.cs create mode 100644 src/BoardZApi/BoardGame.Api/Services/GameService.cs create mode 100644 src/BoardZApi/BoardGame.Api/Services/PlayerService.cs diff --git a/src/BoardZApi/BoardGame.Api/Services/AgeRatingService.cs b/src/BoardZApi/BoardGame.Api/Services/AgeRatingService.cs new file mode 100644 index 0000000..6cdfee5 --- /dev/null +++ b/src/BoardZApi/BoardGame.Api/Services/AgeRatingService.cs @@ -0,0 +1,30 @@ +using BoardGame.Api.Models; +using System; +using System.Collections.Generic; + +namespace BoardGame.Api.Services +{ + internal class AgeRatingService : IDisposable + { + private readonly BoardzContext _dbContext; + + public AgeRatingService() + { + _dbContext = new BoardzContext(); + } + + + public IEnumerable GetAll() + { + return _dbContext.AgeRatings; + } + + public void Dispose() + { + if (_dbContext != null) + { + _dbContext.Dispose(); + } + } + } +} diff --git a/src/BoardZApi/BoardGame.Api/Services/CategoriesService.cs b/src/BoardZApi/BoardGame.Api/Services/CategoriesService.cs new file mode 100644 index 0000000..b986da7 --- /dev/null +++ b/src/BoardZApi/BoardGame.Api/Services/CategoriesService.cs @@ -0,0 +1,132 @@ +using BoardGame.Api.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Data.Entity; + +namespace BoardGame.Api.Services +{ + internal class CategoriesService : IDisposable + { + private readonly BoardzContext _dbContext; + + /// + /// / + /// + public CategoriesService() + { + _dbContext = new BoardzContext(); + } + /// + /// IDisposable + /// + public void Dispose() + { + if(_dbContext != null) + { + _dbContext.Dispose(); + } + } + + /// + /// Return all Categories from a given user + /// + /// + /// + public IEnumerable GetAll(String userName) + { + return _dbContext.Categories.Where(category => category.UserName.Equals(userName, StringComparison.InvariantCultureIgnoreCase)).OrderBy(category => category.Name); + } + + /// + /// Get a category by it's id + /// + /// + /// + /// + public Category GetById(Guid categoryId, String userName) + { + return _dbContext.Categories + .Include(category => category.Games) + .Where(category => category.UserName.Equals(userName, StringComparison.InvariantCultureIgnoreCase)) + .FirstOrDefault(category => category.Id.Equals(categoryId)); + } + + /// + /// Get Category Count + /// + /// + /// + internal int Count(string username) + { + return this.GetAll(username).Count(); + } + + /// + /// Update a category + /// + /// + /// + /// + public bool Update(Category category, String userName) + { + try + { + category.UserName = userName; + _dbContext.Categories.Attach(category); + _dbContext.Entry(category).State = EntityState.Modified; + _dbContext.SaveChanges(); + return true; + } + catch (Exception) + { + return false; + } + } + + + /// + /// Delete a category by it's id + /// + /// + /// + public bool DeleteCategory(Guid categoryId) + { + + var found = _dbContext.Categories.Include(category => category.Games).FirstOrDefault(category => category.Id.Equals(categoryId)); + if (found == null) + { + return false; + } + try + { + if (found.Games.Any()) + { + return false; + } + _dbContext.Entry(found).State = EntityState.Deleted; + _dbContext.SaveChanges(); + return true; + } + catch (Exception) + { + return false; + } + } + + /// + /// Create a new Category + /// + /// + /// + /// + public Guid AddCategory(Category category, string userName) + { + category.Id = Guid.NewGuid(); + category.UserName = userName; + _dbContext.Categories.Add(category); + _dbContext.SaveChanges(); + return category.Id; + } + } +} diff --git a/src/BoardZApi/BoardGame.Api/Services/GameService.cs b/src/BoardZApi/BoardGame.Api/Services/GameService.cs new file mode 100644 index 0000000..6943535 --- /dev/null +++ b/src/BoardZApi/BoardGame.Api/Services/GameService.cs @@ -0,0 +1,133 @@ +using BoardGame.Api.Models; +using System; +using System.Collections.Generic; +using System.Data.Entity; +using System.Linq; + +namespace BoardGame.Api.Services +{ + /// + /// Game Service + /// + public class GameService : IDisposable + { + private readonly BoardzContext _dbContext; + + /// + /// Default CTOR + /// + public GameService() + { + _dbContext = new BoardzContext(); + } + + /// + /// Returns all games for a given user + /// + /// + /// + public IEnumerable GetAll(string userName) + { + return _dbContext.Games + .Include(game => game.AgeRating) + .Include(game => game.Categories) + .Where(game => game.UserName.Equals(userName, StringComparison.InvariantCultureIgnoreCase)); + } + + /// + /// Get a game by it's ID + /// + /// + /// + /// + public Game GetById(Guid gameId, String userName) + { + return _dbContext.Games + .Include(game => game.AgeRating) + .Include(game => game.Categories) + .Where(game => game.UserName.Equals(userName, StringComparison.InvariantCultureIgnoreCase)) + .FirstOrDefault(game => game.Id.Equals(gameId)); + } + + /// + /// Get Games Count + /// + /// + /// + internal int Count(string username) + { + return this.GetAll(username).Count(); + } + + /// + /// Update an existing game + /// + /// + /// + /// + public bool Update(Game game, String userName) + { + try + { + game.UserName = userName; + _dbContext.Games.Attach(game); + _dbContext.Entry(game).State = EntityState.Modified; + _dbContext.SaveChanges(); + return true; + } + catch (Exception) + { + return false; + } + } + + /// + /// Delete a game by it's Id + /// + /// + /// + public bool DeleteGame(Guid gameId) + { + var found = _dbContext.Games.FirstOrDefault(game => game.Id.Equals(gameId)); + if(found == null) + { + return false; + } + try + { + _dbContext.Entry(found).State = EntityState.Deleted; + _dbContext.SaveChanges(); + return true; + } + catch (Exception) + { + return false; + } + } + /// + /// Add a game + /// + /// + /// + /// + public Guid AddGame(Game game, String userName) + { + game.Id = Guid.NewGuid(); + game.UserName = userName; + _dbContext.Games.Add(game); + _dbContext.SaveChanges(); + return game.Id; + } + + /// + /// IDisposable + /// + public void Dispose() + { + if(_dbContext != null) + { + _dbContext.Dispose(); + } + } + } +} diff --git a/src/BoardZApi/BoardGame.Api/Services/PlayerService.cs b/src/BoardZApi/BoardGame.Api/Services/PlayerService.cs new file mode 100644 index 0000000..724ca4f --- /dev/null +++ b/src/BoardZApi/BoardGame.Api/Services/PlayerService.cs @@ -0,0 +1,139 @@ +using BoardGame.Api.Helpers; +using BoardGame.Api.Models; +using System; +using System.Collections.Generic; +using System.Data.Entity; +using System.Data.Entity.Core.Objects; +using System.Linq; + +namespace BoardGame.Api.Services +{ + /// + /// PlayerService + /// + public class PlayerService : IDisposable + { + private readonly BoardzContext _dbContext; + private readonly DistanceCalculator _distanceCalculator; + /// + /// Default CTOR + /// + public PlayerService() + { + _dbContext = new BoardzContext(); + _distanceCalculator = new DistanceCalculator(); + } + + internal IEnumerable GetAll() + { + return _dbContext.Players.Include(player=>player.Coordinate).Include(player=>player.Game); + } + + /// + /// Return a list of playeres (playing since 5 days :D) playing nearby a given coordiante + /// + /// + /// + /// + public IEnumerable FindPlayersNearby(Coordinate queryCoordinates, int radius) + { + var players = _dbContext.Players + .Include(player => player.Coordinate) + .Include(player => player.Game).ToList(); + + return players + .Where(player => player.PlayingSince > DateTime.Now.AddDays(-5)) + .Select(c => new PlayerWithDistance() + { + Player = c, + Distance = _distanceCalculator.CalculateDistance(queryCoordinates, c.Coordinate) + }) + .Where(c => c.Distance <= radius) + .OrderBy(c => c.Distance); + } + + /// + /// Get the number of active players + /// + /// + internal int Count() + { + var players = _dbContext.Players.ToList(); + return players.Where(player => player.PlayingSince.Date.Equals(DateTime.Now.Date)).Count(); + } + + internal Player GetById(Guid playerId) + { + return _dbContext.Players.FirstOrDefault(player => player.Id.Equals(playerId)); + } + + /// + /// Checkin + /// + /// + /// + public Guid AddPlayer(Player player) + { + player.Id = Guid.NewGuid(); + player.PlayingSince = DateTime.Now; + _dbContext.Players.Add(player); + _dbContext.SaveChanges(); + return player.Id; + } + + /// + /// Delete a player by his/her id + /// + /// + /// + internal bool DeletePlayer(Guid playerId) + { + var found = _dbContext.Players.FirstOrDefault(player => player.Id.Equals(playerId)); + if (found == null) + { + return false; + } + try + { + _dbContext.Entry(found).State = EntityState.Deleted; + _dbContext.SaveChanges(); + return true; + } + catch (Exception) + { + return false; + } + } + + /// + /// update an existing player instance + /// + /// + /// + internal bool Update(Player player) + { + try + { + _dbContext.Players.Attach(player); + _dbContext.Entry(player).State = EntityState.Modified; + _dbContext.SaveChanges(); + return true; + } + catch (Exception) + { + return false; + } + } + + /// + /// IDisposable + /// + public void Dispose() + { + if(_dbContext != null) + { + _dbContext.Dispose(); + } + } + } +} From 64b59a38419deed9d659e4302dff8eee7850ef99 Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Sat, 17 Sep 2016 16:26:26 +0200 Subject: [PATCH 16/53] Updated appConfigs to provide ConnectionString --- src/BoardZApi/BoardGame.Host/App.config | 23 +- .../BoardGame.Host/BoardGame.Host.csproj | 9 + src/BoardZApi/BoardGame.Host/Program.cs | 9 + src/BoardZApi/BoardGame.Host/packages.config | 1 + src/BoardZApi/BoardGame.WebHost/web.config | 3 + src/BoardZApi/docs/BoardGame.Api.xml | 476 +++++++++++++++++- 6 files changed, 502 insertions(+), 19 deletions(-) diff --git a/src/BoardZApi/BoardGame.Host/App.config b/src/BoardZApi/BoardGame.Host/App.config index 6d8f92e..c5a30a2 100644 --- a/src/BoardZApi/BoardGame.Host/App.config +++ b/src/BoardZApi/BoardGame.Host/App.config @@ -1,8 +1,15 @@  - - - + + +
+ + + + + + + @@ -35,4 +42,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/BoardZApi/BoardGame.Host/BoardGame.Host.csproj b/src/BoardZApi/BoardGame.Host/BoardGame.Host.csproj index f6b00c4..406d627 100644 --- a/src/BoardZApi/BoardGame.Host/BoardGame.Host.csproj +++ b/src/BoardZApi/BoardGame.Host/BoardGame.Host.csproj @@ -48,6 +48,14 @@ 4 + + ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll + True + + + ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll + True + ..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll True @@ -81,6 +89,7 @@ True + ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll diff --git a/src/BoardZApi/BoardGame.Host/Program.cs b/src/BoardZApi/BoardGame.Host/Program.cs index 4451edd..fdc86b7 100644 --- a/src/BoardZApi/BoardGame.Host/Program.cs +++ b/src/BoardZApi/BoardGame.Host/Program.cs @@ -1,5 +1,6 @@ using System; using BoardGame.Api; +using System.Linq; using Microsoft.Owin.Hosting; namespace BoardGame.Host @@ -8,6 +9,14 @@ class Program { static void Main(string[] args) { + //Ensure EF generates the database + + using (var ctx = new BoardzContext()) + { + Console.WriteLine("Context initialized"); + Console.WriteLine($"There are {ctx.Games.Count()} Games in your database"); + } + using (WebApp.Start("http://+:8080")) { Console.WriteLine("Server is up and running"); diff --git a/src/BoardZApi/BoardGame.Host/packages.config b/src/BoardZApi/BoardGame.Host/packages.config index 7bf6940..9d1151c 100644 --- a/src/BoardZApi/BoardGame.Host/packages.config +++ b/src/BoardZApi/BoardGame.Host/packages.config @@ -1,5 +1,6 @@  + diff --git a/src/BoardZApi/BoardGame.WebHost/web.config b/src/BoardZApi/BoardGame.WebHost/web.config index fd2b39d..638e466 100644 --- a/src/BoardZApi/BoardGame.WebHost/web.config +++ b/src/BoardZApi/BoardGame.WebHost/web.config @@ -4,6 +4,9 @@ http://go.microsoft.com/fwlink/?LinkId=169433 --> + + + diff --git a/src/BoardZApi/docs/BoardGame.Api.xml b/src/BoardZApi/docs/BoardGame.Api.xml index 3cd5f37..6d6bbbe 100644 --- a/src/BoardZApi/docs/BoardGame.Api.xml +++ b/src/BoardZApi/docs/BoardGame.Api.xml @@ -4,56 +4,195 @@ BoardGame.Api - + + + BoardZ EF Context + + + + + default ctor + + + + + Categories Set + + + + + Age Rating Set + + + + + All the Games :allthethings: + + + + + all players set + + + + + all coordinates used by the I'm playing feature + + + + + override ModelCreating + + + + + + BoardZ SQL Azure Config + + + + + default CTOR + + + + + Seed BoardZ Database with some static data + + + + + + AgeRatingController + + + + + Default CTOR + + + + + IDisposable + + + + + + Categories Controller + + + + + default CTOR + + + + + Returns the categories count. + + + + + + Adds a new board game category + + + + + + + Returns a single board game category + + + + + + + Removes a board game category + + + + + + + Updates a board game category + + + + + + + IDisposable + + + + Provides a CRUD api for board games - + - Lists all players + default CTOR + + + + + Lists all games - + Returns the games count. - + Adds a new board game - + Returns a single board game - + Removes a board game - + Updates a board game + + + IDisposable + + + Provides an CRUD api for players + + + default ctor + + Returns a list of players. @@ -102,6 +241,12 @@ + + + IDisposable + + + Status Controller is responsible for exposing the ping endpoint which will be used @@ -113,28 +258,132 @@ Ping endpoint - Just returning a HTTP StatusCode 200 + + + Distance Calculator Logic + + - Adapted from http://www.geodatasource.com/developers/c-sharp - + + Adapted from http://www.geodatasource.com/developers/c-sharp + + + + + + + + AgeRating Model + + + + + AgeRating Id + + + + + Name of the AgeRating + - + + + Color indicator for the age rating + + + + + Games applied to this age rating + + + + + The Game Model + + + + + default ctor + + + Unique identifier - + Name of the board game - + + + Edition of the Game + + + + + List of categories applied to the game + + + + + AgeRatingId + + + + + Age Rating + + + Additional description - + - Packshot URLs + Name of the user who created the game + + + + + Category model + + + + + default ctor + + + + + Category Id + + + + + Name of the Category + + + + + The name of the user who craeated that category + + + + + All games in this category + + + + + Coordinate DataType + + + + + PK @@ -157,25 +406,220 @@ Name of the player + + + FK + + Optional coordinate of the player - + Current game the player is playing + + + Current Game instance + + Base64 Image Url (if player did "i am gaming") + + + Since when is the player playing that game + + Will only be set when using API + + + Model used to transfer nearby players + + + + + The Player instance + + + + + Distance in KM + + + + + / + + + + + IDisposable + + + + + Return all Categories from a given user + + + + + + + Get a category by it's id + + + + + + + + Get Category Count + + + + + + + Update a category + + + + + + + + Delete a category by it's id + + + + + + + Create a new Category + + + + + + + + Game Service + + + + + Default CTOR + + + + + Returns all games for a given user + + + + + + + Get a game by it's ID + + + + + + + + Get Games Count + + + + + + + Update an existing game + + + + + + + + Delete a game by it's Id + + + + + + + Add a game + + + + + + + + IDisposable + + + + + PlayerService + + + + + Default CTOR + + + + + Return a list of playeres (playing since 5 days :D) playing nearby a given coordiante + + + + + + + + Get the number of active players + + + + + + Checkin + + + + + + + Delete a player by his/her id + + + + + + + update an existing player instance + + + + + + + IDisposable + + From 6f7b2575f9b4586dee98f7403d4f660549e82f40 Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Sat, 17 Sep 2016 16:27:05 +0200 Subject: [PATCH 17/53] reflect new API stuff in the Client (applies only to currently existing models) --- src/BoardZ/app/components/games/details.ts | 2 +- src/BoardZ/app/models/player.ts | 2 +- src/BoardZ/app/services/gamesService.ts | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/BoardZ/app/components/games/details.ts b/src/BoardZ/app/components/games/details.ts index 22f7767..3bc3994 100644 --- a/src/BoardZ/app/components/games/details.ts +++ b/src/BoardZ/app/components/games/details.ts @@ -138,7 +138,7 @@ export class GameDetailsComponent implements OnInit { let player = new Player(); player.name = this._loginService.username; - player.boardGameId = this.model.id; + player.gameId = this.model.id; player.coordinate = this._coordinates; player.imageUrl = this._pictureUrl; diff --git a/src/BoardZ/app/models/player.ts b/src/BoardZ/app/models/player.ts index 744b14b..61b5cc8 100644 --- a/src/BoardZ/app/models/player.ts +++ b/src/BoardZ/app/models/player.ts @@ -3,7 +3,7 @@ import {GeoLocation} from './geoLocation'; export class Player{ public id: string; public name: string; - public boardGameId: string; + public gameId: string; public boardGameName: string; public coordinate: GeoLocation; public imageUrl: string; diff --git a/src/BoardZ/app/services/gamesService.ts b/src/BoardZ/app/services/gamesService.ts index e7db363..f54f58a 100644 --- a/src/BoardZ/app/services/gamesService.ts +++ b/src/BoardZ/app/services/gamesService.ts @@ -20,7 +20,7 @@ export class GamesService { } public getAll(): Observable { - return this._http.get('api/boardgames/list').map(response => (response.json())); + return this._http.get('api/games/list').map(response => (response.json())); } public deepClone(game: Game): Game { @@ -32,22 +32,22 @@ export class GamesService { } public getById(id: string): Observable { - return this._http.get(`api/boardgames/single?id=${id}`) + return this._http.get(`api/games/single?id=${id}`) .map(response => response.json()); } public addGame(game: Game): Observable { - return this._http.post(`api/boardgames/add`, JSON.stringify(game), this.getRequestOptions()) + return this._http.post(`api/games/add`, JSON.stringify(game), this.getRequestOptions()) .map(response => response.json()); } public updateGame(game: Game): Observable { - return this._http.put(`api/boardgames/update`, JSON.stringify(game), this.getRequestOptions()) + return this._http.put(`api/games/update`, JSON.stringify(game), this.getRequestOptions()) .map(response => game.id); } public deleteGame(id: string): Observable { - return this._http.delete(`api/boardgames/remove?id=${id}`) + return this._http.delete(`api/games/remove?id=${id}`) .map(response => response.text()); } } From 4daeb559c51697b89e29451fa4be0b38031fea5e Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Sat, 17 Sep 2016 16:58:24 +0200 Subject: [PATCH 18/53] Added missing parts to the boardZ API --- .../Controllers/AgeRatingsController.cs | 11 +++++++++++ .../Controllers/CategoriesController.cs | 12 +++++++++++- .../Controllers/GamesController.cs | 2 +- .../Controllers/PlayersController.cs | 2 +- src/BoardZApi/docs/BoardGame.Api.xml | 18 +++++++++++++++--- 5 files changed, 39 insertions(+), 6 deletions(-) diff --git a/src/BoardZApi/BoardGame.Api/Controllers/AgeRatingsController.cs b/src/BoardZApi/BoardGame.Api/Controllers/AgeRatingsController.cs index bb6790f..0830ccb 100644 --- a/src/BoardZApi/BoardGame.Api/Controllers/AgeRatingsController.cs +++ b/src/BoardZApi/BoardGame.Api/Controllers/AgeRatingsController.cs @@ -25,6 +25,17 @@ public AgeRatingsController() _ageRatingService = new AgeRatingService(); } + /// + /// Lists all ageRatings + /// + /// + [HttpGet] + [ResponseType(typeof(AgeRating[]))] + public IHttpActionResult List() + { + return Ok(_ageRatingService.GetAll()); + } + /// /// IDisposable /// diff --git a/src/BoardZApi/BoardGame.Api/Controllers/CategoriesController.cs b/src/BoardZApi/BoardGame.Api/Controllers/CategoriesController.cs index 060c684..2c9e206 100644 --- a/src/BoardZApi/BoardGame.Api/Controllers/CategoriesController.cs +++ b/src/BoardZApi/BoardGame.Api/Controllers/CategoriesController.cs @@ -24,6 +24,16 @@ public CategoriesController() _categoriesService = new CategoriesService(); } + /// + /// Lists all categories + /// + /// + [HttpGet] + [ResponseType(typeof(Category[]))] + public IHttpActionResult List() + { + return Ok(_categoriesService.GetAll(User.GetCurrentUsernameOrThrow())); + } /// /// Returns the categories count. @@ -31,7 +41,7 @@ public CategoriesController() /// [HttpGet] [ResponseType(typeof(int))] - public IHttpActionResult GameCount() + public IHttpActionResult Count() { return Ok(_categoriesService.Count(User.GetCurrentUsernameOrThrow())); } diff --git a/src/BoardZApi/BoardGame.Api/Controllers/GamesController.cs b/src/BoardZApi/BoardGame.Api/Controllers/GamesController.cs index ab52319..ce317e6 100644 --- a/src/BoardZApi/BoardGame.Api/Controllers/GamesController.cs +++ b/src/BoardZApi/BoardGame.Api/Controllers/GamesController.cs @@ -41,7 +41,7 @@ public IHttpActionResult List() /// [HttpGet] [ResponseType(typeof(int))] - public IHttpActionResult GameCount() + public IHttpActionResult Count() { return Ok(_gameService.Count(User.GetCurrentUsernameOrThrow())); } diff --git a/src/BoardZApi/BoardGame.Api/Controllers/PlayersController.cs b/src/BoardZApi/BoardGame.Api/Controllers/PlayersController.cs index 5d6bf0e..8aacd90 100644 --- a/src/BoardZApi/BoardGame.Api/Controllers/PlayersController.cs +++ b/src/BoardZApi/BoardGame.Api/Controllers/PlayersController.cs @@ -39,7 +39,7 @@ public IHttpActionResult List() /// [HttpGet] [ResponseType(typeof(int))] - public IHttpActionResult PlayerCount() + public IHttpActionResult Count() { return Ok(_playersService.Count()); } diff --git a/src/BoardZApi/docs/BoardGame.Api.xml b/src/BoardZApi/docs/BoardGame.Api.xml index 6d6bbbe..c7dfb71 100644 --- a/src/BoardZApi/docs/BoardGame.Api.xml +++ b/src/BoardZApi/docs/BoardGame.Api.xml @@ -71,6 +71,12 @@ Default CTOR + + + Lists all ageRatings + + + IDisposable @@ -87,7 +93,13 @@ default CTOR - + + + Lists all categories + + + + Returns the categories count. @@ -143,7 +155,7 @@ - + Returns the games count. @@ -199,7 +211,7 @@ - + Returns the playing players count. From 116c7dbbdff5c24cf010c3c1b69319d322aaa514 Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Sat, 17 Sep 2016 16:58:45 +0200 Subject: [PATCH 19/53] Added new types (models and services) / Dashboard Update --- .../app/components/dashboard/dashboard.html | 4 ++ .../app/components/dashboard/dashboard.ts | 14 +++--- src/BoardZ/app/components/login/login.ts | 7 ++- src/BoardZ/app/models/ageRating.ts | 5 ++ src/BoardZ/app/models/category.ts | 4 ++ src/BoardZ/app/modules/config.ts | 6 +++ src/BoardZ/app/services/ageRatingsService.ts | 39 +++++++++++++++ src/BoardZ/app/services/categoriesService.ts | 49 +++++++++++++++++++ src/BoardZ/app/services/dashboardService.ts | 33 +++++++++++++ src/BoardZ/app/services/gamesService.ts | 4 -- src/BoardZ/app/services/playersService.ts | 4 -- 11 files changed, 154 insertions(+), 15 deletions(-) create mode 100644 src/BoardZ/app/models/ageRating.ts create mode 100644 src/BoardZ/app/models/category.ts create mode 100644 src/BoardZ/app/services/ageRatingsService.ts create mode 100644 src/BoardZ/app/services/categoriesService.ts create mode 100644 src/BoardZ/app/services/dashboardService.ts diff --git a/src/BoardZ/app/components/dashboard/dashboard.html b/src/BoardZ/app/components/dashboard/dashboard.html index 87eaf26..4b3aabb 100644 --- a/src/BoardZ/app/components/dashboard/dashboard.html +++ b/src/BoardZ/app/components/dashboard/dashboard.html @@ -9,4 +9,8 @@

Dashboard

+ +
+ +
diff --git a/src/BoardZ/app/components/dashboard/dashboard.ts b/src/BoardZ/app/components/dashboard/dashboard.ts index 0fe6c48..5e6c9f9 100644 --- a/src/BoardZ/app/components/dashboard/dashboard.ts +++ b/src/BoardZ/app/components/dashboard/dashboard.ts @@ -1,6 +1,5 @@ import {Component, OnInit} from '@angular/core'; -import {GamesService} from '../../services/gamesService'; -import {PlayersService} from '../../services/playersService'; +import {DashboardService} from '../../services/dashboardService'; @Component({ moduleId: module.id, @@ -10,16 +9,19 @@ import {PlayersService} from '../../services/playersService'; export class DashboardComponent implements OnInit { public playerCount: string = '-'; public gameCount: string = '-'; + public categoryCount: string = '-'; - constructor(private _gamesService: GamesService, - private _playersService: PlayersService) { + constructor(private _dashboardService: DashboardService) { } public ngOnInit(): any { - this._playersService.getPlayerCount() + this._dashboardService.getPlayerCount() .subscribe(result => this.playerCount = result.toString()); - this._gamesService.getGameCount() + this._dashboardService.getGameCount() .subscribe(result => this.gameCount = result.toString()); + + this._dashboardService.getCategoryCount() + .subscribe(result => this.categoryCount = result.toString()); } } diff --git a/src/BoardZ/app/components/login/login.ts b/src/BoardZ/app/components/login/login.ts index e54fa26..2b3c423 100644 --- a/src/BoardZ/app/components/login/login.ts +++ b/src/BoardZ/app/components/login/login.ts @@ -4,6 +4,7 @@ import {LoginService} from '../../services/loginService'; import {LogService} from '../../services/logService'; import {NotificationService} from '../../services/notificationService'; import {SignalRService} from '../../services/signalrService'; +import {AgeRatingsService} from '../../services/ageRatingsService'; @Component({ moduleId: module.id, @@ -19,7 +20,9 @@ export class LoginComponent { private _loginService: LoginService, private _logService: LogService, private _notificationService: NotificationService, - private _signalRService: SignalRService) { + private _signalRService: SignalRService, + private _ageRatingsService: AgeRatingsService + ) { } public doLogin(): void { @@ -28,6 +31,8 @@ export class LoginComponent { this._loginService.login(this._userName, this._password) .subscribe( () => { + this._ageRatingsService.initialize(); + this._signalRService.start(); this.setError(false); this._router.navigate(['']); diff --git a/src/BoardZ/app/models/ageRating.ts b/src/BoardZ/app/models/ageRating.ts new file mode 100644 index 0000000..db6dfdd --- /dev/null +++ b/src/BoardZ/app/models/ageRating.ts @@ -0,0 +1,5 @@ +export class AgeRating{ + public id: string; + public name:string; + public colorIndicator: string; +} diff --git a/src/BoardZ/app/models/category.ts b/src/BoardZ/app/models/category.ts new file mode 100644 index 0000000..b0237c8 --- /dev/null +++ b/src/BoardZ/app/models/category.ts @@ -0,0 +1,4 @@ +export class Category{ + public id: string; + public name: string; +} diff --git a/src/BoardZ/app/modules/config.ts b/src/BoardZ/app/modules/config.ts index 084666c..81bc7d3 100644 --- a/src/BoardZ/app/modules/config.ts +++ b/src/BoardZ/app/modules/config.ts @@ -37,6 +37,9 @@ import {LocateItComponent} from '../components/locateIt/locateIt'; import {GeolocationService} from '../services/geolocationService'; import {OfflineConfig} from '../offlineConfig'; import {OfflineDetectionService} from '../services/offlineDetectionService'; +import {DashboardService} from '../services/dashboardService'; +import {AgeRatingsService} from '../services/ageRatingsService'; +import {CategoriesService} from '../services/categoriesService'; export namespace ModuleConfiguration { @@ -74,6 +77,9 @@ export namespace ModuleConfiguration { TokenService, LoginService, LogService, + DashboardService, + AgeRatingsService, + CategoriesService, GamesService, PlayersService, NotificationService, diff --git a/src/BoardZ/app/services/ageRatingsService.ts b/src/BoardZ/app/services/ageRatingsService.ts new file mode 100644 index 0000000..dfcd971 --- /dev/null +++ b/src/BoardZ/app/services/ageRatingsService.ts @@ -0,0 +1,39 @@ +import {Injectable} from '@angular/core'; +import {Headers} from '@angular/http'; +import {AuthenticatedHttp} from './authenticatedHttp'; +import {AgeRating} from '../models/ageRating'; + +@Injectable() +export class AgeRatingsService { + + constructor(private _http: AuthenticatedHttp) { + } + + private get _storageKey(): string { + return '_AGE_RATINGS'; + } + + private getRequestOptions() { + let headers = new Headers(); + headers.append('Accept', 'application/json'); + headers.append('Accept', 'text/plain'); + headers.append('Accept', '*/*'); + headers.append('Content-Type', 'application/json;charset=UTF-8'); + + return { headers: headers }; + } + + public initialize(): void { + this._http.get('api/ageratings/list') + .map(response => (response.json())) + .subscribe((ratings) => this.store(ratings)); + } + + private store(ageRatings: Array) { + localStorage.setItem(this._storageKey, JSON.stringify(ageRatings)); + } + + public getAll(): void { + return JSON.parse(localStorage.getItem(this._storageKey) || '[]'); + } +} diff --git a/src/BoardZ/app/services/categoriesService.ts b/src/BoardZ/app/services/categoriesService.ts new file mode 100644 index 0000000..32886ea --- /dev/null +++ b/src/BoardZ/app/services/categoriesService.ts @@ -0,0 +1,49 @@ +import {Injectable} from '@angular/core'; +import {Headers} from '@angular/http'; +import {Observable} from 'rxjs/Observable'; +import {AuthenticatedHttp} from './authenticatedHttp'; +import {Category} from '../models/category'; + +@Injectable() +export class CategoriesService { + constructor(private _http: AuthenticatedHttp) { + } + + private getRequestOptions() { + let headers = new Headers(); + headers.append('Accept', 'application/json'); + headers.append('Accept', 'text/plain'); + headers.append('Accept', '*/*'); + headers.append('Content-Type', 'application/json;charset=UTF-8'); + + return { headers: headers }; + } + + public getAll(): Observable { + return this._http.get('api/categories/list').map(response => (response.json())); + } + + public deepClone(category: Category): Category { + return JSON.parse(JSON.stringify(category)); + } + + public getById(id: string): Observable { + return this._http.get(`api/categories/single?id=${id}`) + .map(response => response.json()); + } + + public addCategory(category: Category): Observable { + return this._http.post(`api/categories/add`, JSON.stringify(category), this.getRequestOptions()) + .map(response => response.json()); + } + + public updateCategory(category: Category): Observable { + return this._http.put(`api/categories/update`, JSON.stringify(category), this.getRequestOptions()) + .map(response => category.id); + } + + public deleteCategory(id: string): Observable { + return this._http.delete(`api/categories/remove?id=${id}`) + .map(response => response.text()); + } +} diff --git a/src/BoardZ/app/services/dashboardService.ts b/src/BoardZ/app/services/dashboardService.ts new file mode 100644 index 0000000..f38bbb8 --- /dev/null +++ b/src/BoardZ/app/services/dashboardService.ts @@ -0,0 +1,33 @@ +import {Injectable} from '@angular/core'; +import {Headers} from '@angular/http'; +import {Observable} from 'rxjs/Observable'; +import {AuthenticatedHttp} from './authenticatedHttp'; + +@Injectable() +export class DashboardService { + constructor(private _http: AuthenticatedHttp) { + } + + private getRequestOptions() { + let headers = new Headers(); + headers.append('Accept', 'application/json'); + headers.append('Accept', 'text/plain'); + headers.append('Accept', '*/*'); + headers.append('Content-Type', 'application/json;charset=UTF-8'); + + return { headers: headers }; + } + + public getGameCount(): Observable { + return this._http.get('api/games/count').map(response => (response.text())); + } + + public getPlayerCount(): Observable { + return this._http.get('api/players/count').map(response => (response.text())); + } + + public getCategoryCount(): Observable { + return this._http.get('api/categories/count').map(response => (response.text())); + } + +} diff --git a/src/BoardZ/app/services/gamesService.ts b/src/BoardZ/app/services/gamesService.ts index f54f58a..c52e510 100644 --- a/src/BoardZ/app/services/gamesService.ts +++ b/src/BoardZ/app/services/gamesService.ts @@ -27,10 +27,6 @@ export class GamesService { return JSON.parse(JSON.stringify(game)); } - public getGameCount(): Observable { - return this.getAll().map(games => games.length); - } - public getById(id: string): Observable { return this._http.get(`api/games/single?id=${id}`) .map(response => response.json()); diff --git a/src/BoardZ/app/services/playersService.ts b/src/BoardZ/app/services/playersService.ts index c353d3d..2e0d9d3 100644 --- a/src/BoardZ/app/services/playersService.ts +++ b/src/BoardZ/app/services/playersService.ts @@ -29,10 +29,6 @@ export class PlayersService { return this._http.get(`api/players/single?id=${id}`).map(response => response.json()); } - public getPlayerCount(): Observable { - return this._http.get('api/players/playercount').map(r => r.json()); - } - public findNearby(radius: number, coordinates: GeoLocation): Observable { return this._http.get(`api/players/FindNearby?radius=${radius}&coordinate.latitude=${coordinates.latitude}&coordinate.longitude=${coordinates.longitude}`) .map(r => { From aa6a17ed216c42769ca4f167fc937da35c5e62e8 Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Sat, 17 Sep 2016 17:22:46 +0200 Subject: [PATCH 20/53] Support for categories added in the Anuglar Client --- .../components/categories/categoryRoot.html | 6 ++ .../app/components/categories/categoryRoot.ts | 8 ++ .../app/components/categories/details.html | 36 ++++++++ .../app/components/categories/details.ts | 88 +++++++++++++++++++ .../app/components/categories/list.html | 29 ++++++ src/BoardZ/app/components/categories/list.ts | 37 ++++++++ src/BoardZ/app/components/sidebar/sidebar.ts | 1 + src/BoardZ/app/models/category.ts | 2 +- src/BoardZ/app/modules/appModule.ts | 3 +- src/BoardZ/app/modules/categoriesModule.ts | 13 +++ src/BoardZ/app/modules/config.ts | 19 +++- .../app/resolvers/categoryDetailsResolver.ts | 23 +++++ src/BoardZ/app/routes/index.ts | 32 ++++++- 13 files changed, 291 insertions(+), 6 deletions(-) create mode 100644 src/BoardZ/app/components/categories/categoryRoot.html create mode 100644 src/BoardZ/app/components/categories/categoryRoot.ts create mode 100644 src/BoardZ/app/components/categories/details.html create mode 100644 src/BoardZ/app/components/categories/details.ts create mode 100644 src/BoardZ/app/components/categories/list.html create mode 100644 src/BoardZ/app/components/categories/list.ts create mode 100644 src/BoardZ/app/modules/categoriesModule.ts create mode 100644 src/BoardZ/app/resolvers/categoryDetailsResolver.ts diff --git a/src/BoardZ/app/components/categories/categoryRoot.html b/src/BoardZ/app/components/categories/categoryRoot.html new file mode 100644 index 0000000..f68ad82 --- /dev/null +++ b/src/BoardZ/app/components/categories/categoryRoot.html @@ -0,0 +1,6 @@ +
+
+

Categories

+
+
+ diff --git a/src/BoardZ/app/components/categories/categoryRoot.ts b/src/BoardZ/app/components/categories/categoryRoot.ts new file mode 100644 index 0000000..a4ebf42 --- /dev/null +++ b/src/BoardZ/app/components/categories/categoryRoot.ts @@ -0,0 +1,8 @@ +import {Component} from '@angular/core'; + +@Component({ + moduleId: module.id, + templateUrl: 'categoryRoot.html' +}) +export class CategoryRootComponent { +} diff --git a/src/BoardZ/app/components/categories/details.html b/src/BoardZ/app/components/categories/details.html new file mode 100644 index 0000000..1d91374 --- /dev/null +++ b/src/BoardZ/app/components/categories/details.html @@ -0,0 +1,36 @@ +
+
+
+
+

New category

+

Category details

+
+
+
+
+ +
+ +
+ Category name is required +
+
+
+
+ +
+
+
+
diff --git a/src/BoardZ/app/components/categories/details.ts b/src/BoardZ/app/components/categories/details.ts new file mode 100644 index 0000000..d38c787 --- /dev/null +++ b/src/BoardZ/app/components/categories/details.ts @@ -0,0 +1,88 @@ +import {Component, OnInit} from '@angular/core'; +import {Router, ActivatedRoute} from '@angular/router'; +import {LogService} from '../../services/logService'; +import {NotificationService} from '../../services/notificationService'; +import {Category} from '../../models/category'; +import {CategoriesService} from '../../services/categoriesService'; + +@Component({ + moduleId: module.id, + selector: 'categoryDetail', + templateUrl: 'details.html' +}) +export class CategoryDetailsComponent implements OnInit { + + private _needsReset: boolean; + private _sending: boolean; + + public active = true; + public model: Category = new Category(); + public originalModel: Category = new Category(); + + constructor(private _logService: LogService, + private _categoriesService: CategoriesService, + private _router: Router, + private route: ActivatedRoute, + private _notificationService: NotificationService) { + } + + public ngOnInit(): any { + this.route.data.forEach((data: { category: Category }) => { + this.originalModel = this._categoriesService.deepClone(this.model = data.category || new Category()); + if (this._needsReset) { + this.reset(); + } + }); + } + + public abort(): void { + this._router.navigate(['/categories/all']); + } + + public reset(): void { + this._needsReset = false; + + // Based on: https://angular.io/docs/ts/latest/guide/forms.html + this.model = this._categoriesService.deepClone(this.originalModel); + + // workaround to re-initialize the actual form controls states + this.active = false; + setTimeout(() => this.active = true, 0); + } + + public saveChanges(): void { + if (this.model.id === null) { + this._categoriesService.addCategory(this.model) + .subscribe( + (newId) => { + this._notificationService.notifySuccess('New category was added.'); + this._router.navigate(['/categories/all']); + }, + () => this._notificationService.notifyError('Could not save new category.') + ); + } else { + this._categoriesService.updateCategory(this.model) + .subscribe((oldId) => { + this._notificationService.notifySuccess('Category data was updated.'); + this._router.navigate(['/categories/all']); + }, + () => { + this._notificationService.notifyError('Could not update category data.'); + } + ); + } + } + + public deleteCategory(): void { + if (window.confirm('Really delete the category "' + this.originalModel.name + '" ?')) { + this._categoriesService.deleteCategory(this.originalModel.id) + .subscribe( + () => { + this._notificationService.notifySuccess('Category data was deleted.'); + this.abort(); + }, + () => this._notificationService.notifyError('Could not delete category data.') + ); + } + } +} diff --git a/src/BoardZ/app/components/categories/list.html b/src/BoardZ/app/components/categories/list.html new file mode 100644 index 0000000..8d95116 --- /dev/null +++ b/src/BoardZ/app/components/categories/list.html @@ -0,0 +1,29 @@ +
+
+
+
+

All Categories

+ +
+ +
+
+ +
+ + + + + + + + + + +
Name
{{cat.name}}
+
+
+
+
diff --git a/src/BoardZ/app/components/categories/list.ts b/src/BoardZ/app/components/categories/list.ts new file mode 100644 index 0000000..f850a3b --- /dev/null +++ b/src/BoardZ/app/components/categories/list.ts @@ -0,0 +1,37 @@ +import {Component, OnInit} from '@angular/core'; +import {Router, ActivatedRoute} from '@angular/router'; + +import {NotificationService} from '../../services/notificationService'; +import {CategoriesService} from '../../services/categoriesService'; +import {Category} from '../../models/category'; +@Component({ + moduleId: module.id, + selector: 'ist', + templateUrl: 'list.html' +}) + +export class CategoryListComponent implements OnInit { + public categories: Category[]; + + constructor(private _categoriesService: CategoriesService, + private _router: Router, + private _route: ActivatedRoute, + private _notificationService: NotificationService) { + } + + public openDetails(category: Category): void { + this._router.navigate(['../details', category.id], { relativeTo: this._route }); + } + + public openAdd(): void { + this._router.navigate(['../new'], { relativeTo: this._route }); + } + + public ngOnInit(): void { + this._categoriesService.getAll() + .subscribe( + (categories) => this.categories = categories, + (err) => this._notificationService.notifyError('Error while fetching category data') + ); + } +} diff --git a/src/BoardZ/app/components/sidebar/sidebar.ts b/src/BoardZ/app/components/sidebar/sidebar.ts index ebbb574..556b96b 100644 --- a/src/BoardZ/app/components/sidebar/sidebar.ts +++ b/src/BoardZ/app/components/sidebar/sidebar.ts @@ -14,6 +14,7 @@ export class SidebarComponent { this.navigationEntries = []; this.navigationEntries.push(new NavigationEntry([''], 'dashboard', 'Dashboard')); this.navigationEntries.push(new NavigationEntry(['/games/all'], 'list', 'Games')); + this.navigationEntries.push(new NavigationEntry(['/categories/all'], 'tags', 'Categories')); this.navigationEntries.push(new NavigationEntry(['/radiussearch'], 'location-arrow', 'Players search')); //this.navigationEntries.push(new NavigationEntry(['/notifications'], 'bell', 'Test Notifcations')); diff --git a/src/BoardZ/app/models/category.ts b/src/BoardZ/app/models/category.ts index b0237c8..daeb70a 100644 --- a/src/BoardZ/app/models/category.ts +++ b/src/BoardZ/app/models/category.ts @@ -1,4 +1,4 @@ export class Category{ - public id: string; + public id: string = null; public name: string; } diff --git a/src/BoardZ/app/modules/appModule.ts b/src/BoardZ/app/modules/appModule.ts index 01fd418..c01ae58 100644 --- a/src/BoardZ/app/modules/appModule.ts +++ b/src/BoardZ/app/modules/appModule.ts @@ -2,9 +2,10 @@ import {NgModule} from '@angular/core'; import {ModuleConfiguration} from './config'; import {SharedModule} from './sharedModule'; import {GamesModule} from './gamesModule'; +import {CategoriesModule} from './categoriesModule'; @NgModule({ - imports: [ ModuleConfiguration.App.imports, SharedModule, GamesModule], + imports: [ModuleConfiguration.App.imports, SharedModule, GamesModule, CategoriesModule], exports: ModuleConfiguration.App.exports, declarations: ModuleConfiguration.App.declarations, providers: ModuleConfiguration.App.providers, diff --git a/src/BoardZ/app/modules/categoriesModule.ts b/src/BoardZ/app/modules/categoriesModule.ts new file mode 100644 index 0000000..295705a --- /dev/null +++ b/src/BoardZ/app/modules/categoriesModule.ts @@ -0,0 +1,13 @@ +import {NgModule} from '@angular/core'; +import {ModuleConfiguration} from './config'; +import {SharedModule} from './sharedModule'; + +@NgModule({ + imports: [ModuleConfiguration.Categories.imports, SharedModule], + exports: ModuleConfiguration.Categories.exports, + declarations: ModuleConfiguration.Categories.declarations, + providers: ModuleConfiguration.Categories.providers +}) +export class CategoriesModule { + +} diff --git a/src/BoardZ/app/modules/config.ts b/src/BoardZ/app/modules/config.ts index 81bc7d3..3c9e277 100644 --- a/src/BoardZ/app/modules/config.ts +++ b/src/BoardZ/app/modules/config.ts @@ -11,7 +11,7 @@ import {BackButtonDirective} from '../directives/backButtonDirective'; import {CloseSidebarOnClickDirective} from '../directives/closeSidebarOnClickDirective'; import {BrowserModule} from '@angular/platform-browser'; import {HttpModule, XHRBackend, ConnectionBackend} from '@angular/http'; -import {APP_ROUTING, appRoutingProviders, GAMES_ROUTING} from '../routes/index'; +import {APP_ROUTING, appRoutingProviders, GAMES_ROUTING, CATEGORIES_ROUTING} from '../routes/index'; import {ApiConfig} from '../apiConfig'; import {HashLocationStrategy, LocationStrategy, CommonModule} from '@angular/common'; import {NativeIntegrationService} from '../services/nativeIntegrationService'; @@ -26,7 +26,6 @@ import {PlatformInformationService} from '../services/platformInformationService import {UiNotificationService} from '../services/uiNotificationService'; import {SignalRService} from '../services/signalrService'; import {GameListComponent} from '../components/games/list'; -import {GameDetailsComponent} from '../components/games/details'; import {GamesRootComponent} from '../components/games/gamesRoot'; import {GameDetailsResolver} from '../resolvers/gameDetailsResolver'; import {CameraService} from '../services/cameraService'; @@ -40,6 +39,11 @@ import {OfflineDetectionService} from '../services/offlineDetectionService'; import {DashboardService} from '../services/dashboardService'; import {AgeRatingsService} from '../services/ageRatingsService'; import {CategoriesService} from '../services/categoriesService'; +import {CategoryRootComponent} from '../components/categories/categoryRoot'; +import {CategoryListComponent} from '../components/categories/list'; +import {GameDetailsComponent} from '../components/games/details'; +import {CategoryDetailsComponent} from '../components/categories/details'; +import {CategoryDetailsResolver} from '../resolvers/categoryDetailsResolver'; export namespace ModuleConfiguration { @@ -102,6 +106,17 @@ export namespace ModuleConfiguration { public static providers = [GameDetailsResolver]; } + export class Categories { + + public static declarations = [CategoryRootComponent, CategoryDetailsComponent, CategoryListComponent]; + + public static imports = [CommonModule, FormsModule, CATEGORIES_ROUTING]; + + public static exports = []; + + public static providers = [CategoryDetailsResolver]; + } + export class Shared { public static declarations = [PictureItComponent, LocateItComponent]; diff --git a/src/BoardZ/app/resolvers/categoryDetailsResolver.ts b/src/BoardZ/app/resolvers/categoryDetailsResolver.ts new file mode 100644 index 0000000..7040ab7 --- /dev/null +++ b/src/BoardZ/app/resolvers/categoryDetailsResolver.ts @@ -0,0 +1,23 @@ +import {Injectable} from '@angular/core'; +import {Resolve, ActivatedRouteSnapshot} from '@angular/router'; +import {Category} from '../models/category'; +import {CategoriesService} from '../services/categoriesService'; + +@Injectable() +export class CategoryDetailsResolver implements Resolve { + constructor(private _categoriesService: CategoriesService) { + } + + resolve(route: ActivatedRouteSnapshot): Promise { + let id = route.params['id']; + return new Promise((resolve) => { + this._categoriesService.getById(id).subscribe(category => { + if (category) { + resolve(category); + } else { + resolve(new Category()); + } + }); + }); + } +} diff --git a/src/BoardZ/app/routes/index.ts b/src/BoardZ/app/routes/index.ts index 5f530aa..618f4b9 100644 --- a/src/BoardZ/app/routes/index.ts +++ b/src/BoardZ/app/routes/index.ts @@ -9,6 +9,10 @@ import {GameDetailsResolver} from '../resolvers/gameDetailsResolver'; import {GameDetailsComponent} from '../components/games/details'; import {GameListComponent} from '../components/games/list'; import {GamesRootComponent} from '../components/games/gamesRoot'; +import {CategoryRootComponent} from '../components/categories/categoryRoot'; +import {CategoryListComponent} from '../components/categories/list'; +import {CategoryDetailsComponent} from '../components/categories/details'; +import {CategoryDetailsResolver} from '../resolvers/categoryDetailsResolver'; const appRootRoutes: Routes = [ @@ -29,21 +33,45 @@ const gameRoutes: Routes = [ { path: 'new', component: GameDetailsComponent, - name: 'CreateGame', data: { displayName: 'Create a new game' } }, { path: 'details/:id', component: GameDetailsComponent, - name: 'GameDetails', + resolve: { game: GameDetailsResolver }, data: { displayName: 'Game details' } } ] } +]; + +const categoryRoutes: Routes = [ + + { + path: 'categories', + component: CategoryRootComponent, + canActivate: [AuthGuard], + children: [ + { path: 'all', component: CategoryListComponent, data: { displayName: 'Category overview' } }, + { + path: 'new', + component: CategoryDetailsComponent, + data: { displayName: 'Create a new category' } + }, + { + path: 'details/:id', + component: CategoryDetailsComponent, + resolve: { category: CategoryDetailsResolver }, + data: { displayName: 'Category details' } + } + ] + } + ]; export const appRoutingProviders: any[] = [AuthGuard]; export const APP_ROUTING: ModuleWithProviders = RouterModule.forRoot(appRootRoutes); export const GAMES_ROUTING: ModuleWithProviders = RouterModule.forChild(gameRoutes); +export const CATEGORIES_ROUTING: ModuleWithProviders = RouterModule.forChild(categoryRoutes); From 30f80b5925c6efc7c02c4883c1eb3ab33152e6d4 Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Sat, 17 Sep 2016 18:11:37 +0200 Subject: [PATCH 21/53] =?UTF-8?q?Some=20models=20need=20=E2=9D=A4=EF=B8=8F?= =?UTF-8?q?fixes=20#12?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BoardZ/app/models/category.ts | 22 +++++++++++++++++++++- src/BoardZ/app/models/entityState.ts | 6 ++++++ src/BoardZ/app/models/game.ts | 21 +++++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 src/BoardZ/app/models/entityState.ts diff --git a/src/BoardZ/app/models/category.ts b/src/BoardZ/app/models/category.ts index daeb70a..c897e95 100644 --- a/src/BoardZ/app/models/category.ts +++ b/src/BoardZ/app/models/category.ts @@ -1,4 +1,24 @@ -export class Category{ +import {EntityState} from './entityState'; + +export class Category { + + constructor() { + this.entityState = EntityState.Clean; + } + public id: string = null; public name: string; + public entityState: EntityState; + public rowVersion: number; + + public static fromRawJson(rawJson: any): Category { + if (!rawJson) { + return new Category(); + } + let instance: Category = new Category(); + instance.id = rawJson.hasOwnProperty('id') ? rawJson.id : null; + instance.name = rawJson.hasOwnProperty('name') ? rawJson.name : null; + instance.rowVersion = rawJson.hasOwnProperty('rowVersion') ? rawJson.rowVersion : -1; + return instance; + } } diff --git a/src/BoardZ/app/models/entityState.ts b/src/BoardZ/app/models/entityState.ts new file mode 100644 index 0000000..753a86e --- /dev/null +++ b/src/BoardZ/app/models/entityState.ts @@ -0,0 +1,6 @@ +export enum EntityState{ + Clean = 0, + New = 1, + Modified = 2, + Deleted = 3 +} diff --git a/src/BoardZ/app/models/game.ts b/src/BoardZ/app/models/game.ts index 71432e3..784b573 100644 --- a/src/BoardZ/app/models/game.ts +++ b/src/BoardZ/app/models/game.ts @@ -1,6 +1,27 @@ +import {EntityState} from './entityState'; export class Game { + + constructor() { + this.entityState = EntityState.Clean; + } + public id: string = null; public name: string; public description: string; public userName: string; + public entityState: EntityState; + public rowVersion: number; + + public static fromRawJson(rawJson: any): Game { + if (!rawJson) { + return new Game(); + } + let instance: Game = new Game(); + instance.id = rawJson.hasOwnProperty('id') ? rawJson.id : null; + instance.name = rawJson.hasOwnProperty('name') ? rawJson.name : null; + instance.description = rawJson.hasOwnProperty('description') ? rawJson.description : null; + instance.userName = rawJson.hasOwnProperty('userName') ? rawJson.userName : null; + instance.rowVersion = rawJson.hasOwnProperty('rowVersion')? rawJson.rowVersion: -1; + return instance; + } } From 8f03008eb6ce871cc8de59ab69b67aaaf2d6b66c Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Sat, 17 Sep 2016 18:13:48 +0200 Subject: [PATCH 22/53] Transform Raw JSON objects to strongly typed instance --- src/BoardZ/app/services/categoriesService.ts | 4 +++- src/BoardZ/app/services/gamesService.ts | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/BoardZ/app/services/categoriesService.ts b/src/BoardZ/app/services/categoriesService.ts index 32886ea..100fdb2 100644 --- a/src/BoardZ/app/services/categoriesService.ts +++ b/src/BoardZ/app/services/categoriesService.ts @@ -20,7 +20,9 @@ export class CategoriesService { } public getAll(): Observable { - return this._http.get('api/categories/list').map(response => (response.json())); + return this._http.get('api/categories/list') + .map(response => (response.json())) + .map(rawCategories => rawCategories.map(rawCategory => Category.fromRawJson(rawCategory))); } public deepClone(category: Category): Category { diff --git a/src/BoardZ/app/services/gamesService.ts b/src/BoardZ/app/services/gamesService.ts index c52e510..aa79ed8 100644 --- a/src/BoardZ/app/services/gamesService.ts +++ b/src/BoardZ/app/services/gamesService.ts @@ -20,7 +20,9 @@ export class GamesService { } public getAll(): Observable { - return this._http.get('api/games/list').map(response => (response.json())); + return this._http.get('api/games/list') + .map(response => response.json()) + .map(rawGames => rawGames.map(game => Game.fromRawJson(game))); } public deepClone(game: Game): Game { From a281f0458c81a53e9a22c3c812ebffdc27b89bb3 Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Sat, 17 Sep 2016 20:22:11 +0200 Subject: [PATCH 23/53] =?UTF-8?q?-EntityState=20+ModelState=20=F0=9F=92=84?= =?UTF-8?q?=20fixes=20#21?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BoardZ/app/models/category.ts | 6 +++--- src/BoardZ/app/models/game.ts | 6 +++--- src/BoardZ/app/models/{entityState.ts => modelState.ts} | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) rename src/BoardZ/app/models/{entityState.ts => modelState.ts} (71%) diff --git a/src/BoardZ/app/models/category.ts b/src/BoardZ/app/models/category.ts index c897e95..ba5315e 100644 --- a/src/BoardZ/app/models/category.ts +++ b/src/BoardZ/app/models/category.ts @@ -1,14 +1,14 @@ -import {EntityState} from './entityState'; +import {ModelState} from './entityState'; export class Category { constructor() { - this.entityState = EntityState.Clean; + this.state = ModelState.Clean; } public id: string = null; public name: string; - public entityState: EntityState; + public state: ModelState; public rowVersion: number; public static fromRawJson(rawJson: any): Category { diff --git a/src/BoardZ/app/models/game.ts b/src/BoardZ/app/models/game.ts index 784b573..e8792c7 100644 --- a/src/BoardZ/app/models/game.ts +++ b/src/BoardZ/app/models/game.ts @@ -1,15 +1,15 @@ -import {EntityState} from './entityState'; +import {ModelState} from './entityState'; export class Game { constructor() { - this.entityState = EntityState.Clean; + this.state = ModelState.Clean; } public id: string = null; public name: string; public description: string; public userName: string; - public entityState: EntityState; + public state: ModelState; public rowVersion: number; public static fromRawJson(rawJson: any): Game { diff --git a/src/BoardZ/app/models/entityState.ts b/src/BoardZ/app/models/modelState.ts similarity index 71% rename from src/BoardZ/app/models/entityState.ts rename to src/BoardZ/app/models/modelState.ts index 753a86e..ebef8e6 100644 --- a/src/BoardZ/app/models/entityState.ts +++ b/src/BoardZ/app/models/modelState.ts @@ -1,4 +1,4 @@ -export enum EntityState{ +export enum ModelState{ Clean = 0, New = 1, Modified = 2, From 85488b1eefbe9e8f4629caad3a492e3feb9d7799 Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Sat, 17 Sep 2016 22:41:20 +0200 Subject: [PATCH 24/53] age rating and categories --- src/BoardZ/app/apiConfig.ts | 3 +- .../app/components/categories/list.html | 2 ++ src/BoardZ/app/components/games/details.html | 16 ++++++++++ src/BoardZ/app/components/games/details.ts | 24 +++++++++++++- src/BoardZ/app/models/ageRating.ts | 12 +++++++ src/BoardZ/app/models/category.ts | 12 ++++--- src/BoardZ/app/models/game.ts | 24 ++++++++++---- src/BoardZ/app/services/ageRatingsService.ts | 4 +-- src/BoardZApi/BoardGame.Api/BoardzContext.cs | 12 ++++--- .../Controllers/AgeRatingsController.cs | 5 +-- .../Controllers/CategoriesController.cs | 6 ++-- .../Controllers/GamesController.cs | 12 ++++--- .../BoardGame.Api/Models/AgeRating.cs | 5 ++- .../BoardGame.Api/Models/Category.cs | 19 +++++++++++- src/BoardZApi/BoardGame.Api/Models/Game.cs | 3 +- .../Services/AgeRatingService.cs | 4 ++- .../Services/CategoriesService.cs | 5 +-- .../BoardGame.Api/Services/GameService.cs | 31 ++++++++++++------- src/BoardZApi/BoardGame.Host/Program.cs | 9 +----- .../BoardGame.WebHost.csproj | 1 + src/BoardZApi/docs/BoardGame.Api.xml | 10 ++++++ 21 files changed, 167 insertions(+), 52 deletions(-) diff --git a/src/BoardZ/app/apiConfig.ts b/src/BoardZ/app/apiConfig.ts index 4153d98..9051445 100644 --- a/src/BoardZ/app/apiConfig.ts +++ b/src/BoardZ/app/apiConfig.ts @@ -1,6 +1,7 @@ export class ApiConfig { public get rootUrl(): string { - return 'https://offline-boardz.azurewebsites.net/'; + //return 'https://offline-boardz.azurewebsites.net/'; + return 'http://10.211.55.7:8080/' } } diff --git a/src/BoardZ/app/components/categories/list.html b/src/BoardZ/app/components/categories/list.html index 8d95116..4d43abf 100644 --- a/src/BoardZ/app/components/categories/list.html +++ b/src/BoardZ/app/components/categories/list.html @@ -16,10 +16,12 @@

All Categories

Name + Games in this Category {{cat.name}} + {{cat.numberOfGames}} diff --git a/src/BoardZ/app/components/games/details.html b/src/BoardZ/app/components/games/details.html index 2fcba42..10e160b 100644 --- a/src/BoardZ/app/components/games/details.html +++ b/src/BoardZ/app/components/games/details.html @@ -18,6 +18,22 @@

Game details

+
+ +
+ +
+
+
+ +
+ +
+
diff --git a/src/BoardZ/app/components/games/details.ts b/src/BoardZ/app/components/games/details.ts index 3bc3994..a6378fe 100644 --- a/src/BoardZ/app/components/games/details.ts +++ b/src/BoardZ/app/components/games/details.ts @@ -11,6 +11,10 @@ import {GeoLocation} from '../../models/geoLocation'; import {LoginService} from '../../services/loginService'; import {Notification} from '../../models/notification'; import {NotificationType} from '../../models/notificationType'; +import {AgeRating} from '../../models/ageRating'; +import {AgeRatingsService} from '../../services/ageRatingsService'; +import {Category} from '../../models/category'; +import {CategoriesService} from '../../services/categoriesService'; @Component({ moduleId: module.id, @@ -23,24 +27,31 @@ export class GameDetailsComponent implements OnInit { private _pictureUrl: string = ''; private _coordinates: GeoLocation = null; private _sending: boolean; - + public ageRatings: Array; + public categories: Array; public active = true; public model: Game = new Game(); public originalModel: Game = new Game(); + public selectedCategories: Array = []; constructor(private _logService: LogService, private _gameService: GamesService, private _router: Router, private route: ActivatedRoute, private _notificationService: NotificationService, + private _categoriesService: CategoriesService, + private _ageRatingsService: AgeRatingsService, private _playersService: PlayersService, private _signalRService: SignalRService, private _loginService: LoginService) { } public ngOnInit(): any { + this._categoriesService.getAll().subscribe(cats=>this.categories = cats); + this.ageRatings = this._ageRatingsService.getAll(); this.route.data.forEach((data: { game: Game }) => { this.originalModel = this._gameService.deepClone(this.model = data.game || new Game()); + this.selectedCategories = this.originalModel.categories.map(c=>c.id); if (this._needsReset) { this.reset(); } @@ -78,7 +89,18 @@ export class GameDetailsComponent implements OnInit { setTimeout(() => this.active = true, 0); } + public saveChanges(): void { + + this.model.categories = this.categories.filter(cat => this.selectedCategories.indexOf(cat.id) >-1); + + if (this.model.ageRatingId) { + let found = this.ageRatings.filter(ar=>ar.id === this.model.ageRatingId); + if (found && found.length > 0) { + this.model.ageRating = found[0]; + } + + } if (this.model.id === null) { this._gameService.addGame(this.model) .subscribe( diff --git a/src/BoardZ/app/models/ageRating.ts b/src/BoardZ/app/models/ageRating.ts index db6dfdd..4eb7d1b 100644 --- a/src/BoardZ/app/models/ageRating.ts +++ b/src/BoardZ/app/models/ageRating.ts @@ -2,4 +2,16 @@ export class AgeRating{ public id: string; public name:string; public colorIndicator: string; + + + public static fromRawJson(rawJson: any): AgeRating{ + if(!rawJson){ + return new AgeRating(); + } + let instance: AgeRating = new AgeRating(); + instance.id = rawJson.id || null; + instance.name = rawJson.name || null; + instance.colorIndicator = rawJson.colorIndicator || null; + return instance; + } } diff --git a/src/BoardZ/app/models/category.ts b/src/BoardZ/app/models/category.ts index ba5315e..4ad6a22 100644 --- a/src/BoardZ/app/models/category.ts +++ b/src/BoardZ/app/models/category.ts @@ -1,4 +1,4 @@ -import {ModelState} from './entityState'; +import {ModelState} from './modelState'; export class Category { @@ -9,16 +9,20 @@ export class Category { public id: string = null; public name: string; public state: ModelState; + public numberOfGames: number; public rowVersion: number; + + public static fromRawJson(rawJson: any): Category { if (!rawJson) { return new Category(); } let instance: Category = new Category(); - instance.id = rawJson.hasOwnProperty('id') ? rawJson.id : null; - instance.name = rawJson.hasOwnProperty('name') ? rawJson.name : null; - instance.rowVersion = rawJson.hasOwnProperty('rowVersion') ? rawJson.rowVersion : -1; + instance.id = rawJson.id || null; + instance.name = rawJson.name || null; + instance.rowVersion = rawJson.rowVersion || -1; + instance.numberOfGames = rawJson.numberOfGames || 0; return instance; } } diff --git a/src/BoardZ/app/models/game.ts b/src/BoardZ/app/models/game.ts index e8792c7..c73b47f 100644 --- a/src/BoardZ/app/models/game.ts +++ b/src/BoardZ/app/models/game.ts @@ -1,8 +1,11 @@ -import {ModelState} from './entityState'; +import {ModelState} from './modelState'; +import {AgeRating} from './ageRating'; +import {Category} from './category'; export class Game { constructor() { this.state = ModelState.Clean; + this.categories = []; } public id: string = null; @@ -10,18 +13,27 @@ export class Game { public description: string; public userName: string; public state: ModelState; + public ageRatingId: string; + public ageRating: AgeRating; public rowVersion: number; + public categories: Array; + public static fromRawJson(rawJson: any): Game { if (!rawJson) { return new Game(); } let instance: Game = new Game(); - instance.id = rawJson.hasOwnProperty('id') ? rawJson.id : null; - instance.name = rawJson.hasOwnProperty('name') ? rawJson.name : null; - instance.description = rawJson.hasOwnProperty('description') ? rawJson.description : null; - instance.userName = rawJson.hasOwnProperty('userName') ? rawJson.userName : null; - instance.rowVersion = rawJson.hasOwnProperty('rowVersion')? rawJson.rowVersion: -1; + instance.id = rawJson.id || null; + instance.name = rawJson.name || null; + instance.ageRating = AgeRating.fromRawJson(rawJson.ageRating); + instance.ageRatingId = instance.ageRating.id || rawJson.ageRatingId; + if(rawJson.categories){ + instance.categories = rawJson.categories.map(rawCategory=> Category.fromRawJson(rawCategory)); + } + instance.description = rawJson.description || null; + instance.userName = rawJson.userName || null; + instance.rowVersion = rawJson.rowVersion || null; return instance; } } diff --git a/src/BoardZ/app/services/ageRatingsService.ts b/src/BoardZ/app/services/ageRatingsService.ts index dfcd971..8d1bad1 100644 --- a/src/BoardZ/app/services/ageRatingsService.ts +++ b/src/BoardZ/app/services/ageRatingsService.ts @@ -33,7 +33,7 @@ export class AgeRatingsService { localStorage.setItem(this._storageKey, JSON.stringify(ageRatings)); } - public getAll(): void { - return JSON.parse(localStorage.getItem(this._storageKey) || '[]'); + public getAll(): Array{ + return JSON.parse(localStorage.getItem(this._storageKey) || '[]').map(rawRating=>AgeRating.fromRawJson(rawRating)); } } diff --git a/src/BoardZApi/BoardGame.Api/BoardzContext.cs b/src/BoardZApi/BoardGame.Api/BoardzContext.cs index ddbf45b..73da48a 100644 --- a/src/BoardZApi/BoardGame.Api/BoardzContext.cs +++ b/src/BoardZApi/BoardGame.Api/BoardzContext.cs @@ -18,6 +18,8 @@ public BoardzContext() : base("name=BoardZDatabase") { Database.SetInitializer(new BoardzDatabaseInitializer()); + Configuration.LazyLoadingEnabled = false; + Configuration.ProxyCreationEnabled = false; } /// /// Categories Set @@ -42,7 +44,7 @@ public BoardzContext() : /// /// all coordinates used by the I'm playing feature /// - public DbSet Coordinates { get; set; } + public DbSet Coordinates { get; set; } /// /// override ModelCreating @@ -56,15 +58,15 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) .ToTable("Games") .HasKey(game => game.Id) .HasOptional(game => game.AgeRating) - .WithMany(ageRating => ageRating.Games) - .HasForeignKey(game=> game.AgeRatingId); + .WithMany(ageRating => ageRating.Games) + .HasForeignKey(game => game.AgeRatingId); modelBuilder.Entity() .Property(game => game.UserName) .HasColumnAnnotation("GameUserNameIndex", new IndexAnnotation(new IndexAttribute())); modelBuilder.Entity() - .HasMany(game => game.Categories) + .HasMany(game => game.Categories) .WithMany(category => category.Games) .Map(gameCategories => { @@ -80,6 +82,8 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) modelBuilder.Entity() .ToTable("Categories") .HasKey(category => category.Id) + .Ignore(category => category.GameNames) + .Ignore(category => category.NumberOfGames) .Property(category => category.UserName) .HasColumnAnnotation("CategoryUserNameIndex", new IndexAnnotation(new IndexAttribute())); diff --git a/src/BoardZApi/BoardGame.Api/Controllers/AgeRatingsController.cs b/src/BoardZApi/BoardGame.Api/Controllers/AgeRatingsController.cs index 0830ccb..18c6925 100644 --- a/src/BoardZApi/BoardGame.Api/Controllers/AgeRatingsController.cs +++ b/src/BoardZApi/BoardGame.Api/Controllers/AgeRatingsController.cs @@ -1,4 +1,5 @@ -using BoardGame.Api.Models; +using BoardGame.Api.Helpers; +using BoardGame.Api.Models; using BoardGame.Api.Services; using System; using System.Collections.Generic; @@ -33,7 +34,7 @@ public AgeRatingsController() [ResponseType(typeof(AgeRating[]))] public IHttpActionResult List() { - return Ok(_ageRatingService.GetAll()); + return Ok(_ageRatingService.GetAll(User.GetCurrentUsernameOrThrow())); } /// diff --git a/src/BoardZApi/BoardGame.Api/Controllers/CategoriesController.cs b/src/BoardZApi/BoardGame.Api/Controllers/CategoriesController.cs index 2c9e206..465ead7 100644 --- a/src/BoardZApi/BoardGame.Api/Controllers/CategoriesController.cs +++ b/src/BoardZApi/BoardGame.Api/Controllers/CategoriesController.cs @@ -2,6 +2,7 @@ using BoardGame.Api.Models; using BoardGame.Api.Services; using System; +using System.Collections.Generic; using System.Web.Http; using System.Web.Http.Description; @@ -29,10 +30,9 @@ public CategoriesController() /// /// [HttpGet] - [ResponseType(typeof(Category[]))] - public IHttpActionResult List() + public IEnumerable List() { - return Ok(_categoriesService.GetAll(User.GetCurrentUsernameOrThrow())); + return _categoriesService.GetAll(User.GetCurrentUsernameOrThrow()); } /// diff --git a/src/BoardZApi/BoardGame.Api/Controllers/GamesController.cs b/src/BoardZApi/BoardGame.Api/Controllers/GamesController.cs index ce317e6..1a91961 100644 --- a/src/BoardZApi/BoardGame.Api/Controllers/GamesController.cs +++ b/src/BoardZApi/BoardGame.Api/Controllers/GamesController.cs @@ -4,6 +4,7 @@ using BoardGame.Api.Helpers; using BoardGame.Api.Services; using BoardGame.Api.Models; +using System.Collections.Generic; namespace BoardGame.Api.Controllers { @@ -11,7 +12,7 @@ namespace BoardGame.Api.Controllers /// Provides a CRUD api for board games /// [Authorize] - public class GamesController : ApiController, IDisposable + public class GamesController : ApiController { private readonly GameService _gameService; @@ -28,11 +29,12 @@ public GamesController() /// /// [HttpGet] - [ResponseType(typeof(Game[]))] - public IHttpActionResult List() + public IEnumerable List() { var username = User.GetCurrentUsernameOrThrow(); - return Ok(_gameService.GetAll(username)); + var games = _gameService.GetAll(username); + + return games; } /// @@ -100,6 +102,7 @@ public IHttpActionResult Remove(Guid id) [HttpPut] public IHttpActionResult Update(Game game) { + var success =_gameService.Update(game, User.GetCurrentUsernameOrThrow()); if (!success) { @@ -120,5 +123,6 @@ protected override void Dispose(bool disposing) _gameService.Dispose(); } } + } } \ No newline at end of file diff --git a/src/BoardZApi/BoardGame.Api/Models/AgeRating.cs b/src/BoardZApi/BoardGame.Api/Models/AgeRating.cs index a1879fe..989b842 100644 --- a/src/BoardZApi/BoardGame.Api/Models/AgeRating.cs +++ b/src/BoardZApi/BoardGame.Api/Models/AgeRating.cs @@ -1,4 +1,5 @@ -using System; +using Newtonsoft.Json; +using System; using System.Collections.Generic; namespace BoardGame.Api.Models @@ -27,6 +28,8 @@ public class AgeRating /// /// Games applied to this age rating /// + [JsonIgnore] public virtual ICollection Games { get; set; } + } } diff --git a/src/BoardZApi/BoardGame.Api/Models/Category.cs b/src/BoardZApi/BoardGame.Api/Models/Category.cs index 9bf485f..7228a10 100644 --- a/src/BoardZApi/BoardGame.Api/Models/Category.cs +++ b/src/BoardZApi/BoardGame.Api/Models/Category.cs @@ -41,6 +41,23 @@ public Category() /// /// All games in this category /// - public virtual ICollection Games { get;set; } + [JsonIgnore] + public virtual ICollection Games { get; set; } + + /// + /// List with Games in that category + /// + public IList GameNames + { + get + { + return this.Games.Where(game => game.UserName.Equals(this.UserName,StringComparison.InvariantCultureIgnoreCase)).Select(game => game.Name).ToList(); + } + } + + /// + /// NumberOfGames + /// + public int NumberOfGames => this.GameNames.Count; } } diff --git a/src/BoardZApi/BoardGame.Api/Models/Game.cs b/src/BoardZApi/BoardGame.Api/Models/Game.cs index fb1089e..db14544 100644 --- a/src/BoardZApi/BoardGame.Api/Models/Game.cs +++ b/src/BoardZApi/BoardGame.Api/Models/Game.cs @@ -14,7 +14,7 @@ public class Game /// public Game() { - this.Categories = new HashSet(); + } /// /// Unique identifier @@ -40,6 +40,7 @@ public Game() /// AgeRatingId /// public Guid? AgeRatingId { get; set; } + /// /// Age Rating /// diff --git a/src/BoardZApi/BoardGame.Api/Services/AgeRatingService.cs b/src/BoardZApi/BoardGame.Api/Services/AgeRatingService.cs index 6cdfee5..86705e2 100644 --- a/src/BoardZApi/BoardGame.Api/Services/AgeRatingService.cs +++ b/src/BoardZApi/BoardGame.Api/Services/AgeRatingService.cs @@ -1,6 +1,8 @@ using BoardGame.Api.Models; using System; +using System.Linq; using System.Collections.Generic; +using System.Data.Entity; namespace BoardGame.Api.Services { @@ -14,7 +16,7 @@ public AgeRatingService() } - public IEnumerable GetAll() + public IEnumerable GetAll(string userName) { return _dbContext.AgeRatings; } diff --git a/src/BoardZApi/BoardGame.Api/Services/CategoriesService.cs b/src/BoardZApi/BoardGame.Api/Services/CategoriesService.cs index b986da7..2b226f4 100644 --- a/src/BoardZApi/BoardGame.Api/Services/CategoriesService.cs +++ b/src/BoardZApi/BoardGame.Api/Services/CategoriesService.cs @@ -35,7 +35,8 @@ public void Dispose() /// public IEnumerable GetAll(String userName) { - return _dbContext.Categories.Where(category => category.UserName.Equals(userName, StringComparison.InvariantCultureIgnoreCase)).OrderBy(category => category.Name); + return _dbContext.Categories.Include(category=>category.Games) + .Where(category => category.UserName.Equals(userName, StringComparison.InvariantCultureIgnoreCase)).OrderBy(category => category.Name); } /// @@ -47,7 +48,7 @@ public IEnumerable GetAll(String userName) public Category GetById(Guid categoryId, String userName) { return _dbContext.Categories - .Include(category => category.Games) + .Include(category => category.Games.Where(game=>game.UserName.Equals(userName,StringComparison.InvariantCultureIgnoreCase))) .Where(category => category.UserName.Equals(userName, StringComparison.InvariantCultureIgnoreCase)) .FirstOrDefault(category => category.Id.Equals(categoryId)); } diff --git a/src/BoardZApi/BoardGame.Api/Services/GameService.cs b/src/BoardZApi/BoardGame.Api/Services/GameService.cs index 6943535..4223dfb 100644 --- a/src/BoardZApi/BoardGame.Api/Services/GameService.cs +++ b/src/BoardZApi/BoardGame.Api/Services/GameService.cs @@ -27,11 +27,11 @@ public GameService() /// /// public IEnumerable GetAll(string userName) - { - return _dbContext.Games - .Include(game => game.AgeRating) - .Include(game => game.Categories) - .Where(game => game.UserName.Equals(userName, StringComparison.InvariantCultureIgnoreCase)); + { + return _dbContext.Games + .Include(game => game.AgeRating) + .Include(game => game.Categories) + .Where(game => game.UserName.Equals(userName, StringComparison.InvariantCultureIgnoreCase)).ToList(); } /// @@ -43,10 +43,9 @@ public IEnumerable GetAll(string userName) public Game GetById(Guid gameId, String userName) { return _dbContext.Games - .Include(game => game.AgeRating) - .Include(game => game.Categories) - .Where(game => game.UserName.Equals(userName, StringComparison.InvariantCultureIgnoreCase)) - .FirstOrDefault(game => game.Id.Equals(gameId)); + .Include(game => game.AgeRating) + .Include(game => game.Categories) + .FirstOrDefault(game => game.UserName.Equals(userName, StringComparison.InvariantCultureIgnoreCase) && game.Id.Equals(gameId)); } /// @@ -70,8 +69,18 @@ public bool Update(Game game, String userName) try { game.UserName = userName; - _dbContext.Games.Attach(game); - _dbContext.Entry(game).State = EntityState.Modified; + var dbEntity = this.GetById(game.Id, userName); + if(dbEntity == null) + { + return false; + } + _dbContext.Entry(dbEntity).CurrentValues.SetValues(game); + dbEntity.Categories.Clear(); + foreach (var category in game.Categories) + { + var dbCategory = _dbContext.Categories.Find(category.Id); + dbEntity.Categories.Add(dbCategory); + } _dbContext.SaveChanges(); return true; } diff --git a/src/BoardZApi/BoardGame.Host/Program.cs b/src/BoardZApi/BoardGame.Host/Program.cs index fdc86b7..2b03b0b 100644 --- a/src/BoardZApi/BoardGame.Host/Program.cs +++ b/src/BoardZApi/BoardGame.Host/Program.cs @@ -9,14 +9,7 @@ class Program { static void Main(string[] args) { - //Ensure EF generates the database - - using (var ctx = new BoardzContext()) - { - Console.WriteLine("Context initialized"); - Console.WriteLine($"There are {ctx.Games.Count()} Games in your database"); - } - + using (WebApp.Start("http://+:8080")) { Console.WriteLine("Server is up and running"); diff --git a/src/BoardZApi/BoardGame.WebHost/BoardGame.WebHost.csproj b/src/BoardZApi/BoardGame.WebHost/BoardGame.WebHost.csproj index 81728c5..743d3df 100644 --- a/src/BoardZApi/BoardGame.WebHost/BoardGame.WebHost.csproj +++ b/src/BoardZApi/BoardGame.WebHost/BoardGame.WebHost.csproj @@ -127,6 +127,7 @@ + diff --git a/src/BoardZApi/docs/BoardGame.Api.xml b/src/BoardZApi/docs/BoardGame.Api.xml index c7dfb71..2c005e2 100644 --- a/src/BoardZApi/docs/BoardGame.Api.xml +++ b/src/BoardZApi/docs/BoardGame.Api.xml @@ -388,6 +388,16 @@ All games in this category + + + List with Games in that category + + + + + NumberOfGames + + Coordinate DataType From 3afdac62c74bb5d3c11e72765d26fb0af6199099 Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Sat, 17 Sep 2016 22:57:21 +0200 Subject: [PATCH 25/53] Added RowVersion (EF-Migrations activated) --- .../BoardGame.Api/BoardGame.Api.csproj | 17 +++ src/BoardZApi/BoardGame.Api/BoardzContext.cs | 4 + .../201609171250159_InitialCreate.Designer.cs | 29 ++++ .../201609171250159_InitialCreate.cs | 107 +++++++++++++++ .../201609171250159_InitialCreate.resx | 126 ++++++++++++++++++ .../201609172056012_AddRowVersion.Designer.cs | 29 ++++ .../201609172056012_AddRowVersion.cs | 20 +++ .../201609172056012_AddRowVersion.resx | 126 ++++++++++++++++++ .../BoardGame.Api/Migrations/Configuration.cs | 32 +++++ .../BoardGame.Api/Models/Category.cs | 7 +- src/BoardZApi/BoardGame.Api/Models/Game.cs | 5 + src/BoardZApi/BoardGame.Api/app.config | 6 +- src/BoardZApi/docs/BoardGame.Api.xml | 10 ++ 13 files changed, 516 insertions(+), 2 deletions(-) create mode 100644 src/BoardZApi/BoardGame.Api/Migrations/201609171250159_InitialCreate.Designer.cs create mode 100644 src/BoardZApi/BoardGame.Api/Migrations/201609171250159_InitialCreate.cs create mode 100644 src/BoardZApi/BoardGame.Api/Migrations/201609171250159_InitialCreate.resx create mode 100644 src/BoardZApi/BoardGame.Api/Migrations/201609172056012_AddRowVersion.Designer.cs create mode 100644 src/BoardZApi/BoardGame.Api/Migrations/201609172056012_AddRowVersion.cs create mode 100644 src/BoardZApi/BoardGame.Api/Migrations/201609172056012_AddRowVersion.resx create mode 100644 src/BoardZApi/BoardGame.Api/Migrations/Configuration.cs diff --git a/src/BoardZApi/BoardGame.Api/BoardGame.Api.csproj b/src/BoardZApi/BoardGame.Api/BoardGame.Api.csproj index bb40930..c357082 100644 --- a/src/BoardZApi/BoardGame.Api/BoardGame.Api.csproj +++ b/src/BoardZApi/BoardGame.Api/BoardGame.Api.csproj @@ -136,6 +136,15 @@ + + + 201609171250159_InitialCreate.cs + + + + 201609172056012_AddRowVersion.cs + + @@ -175,6 +184,14 @@ Models\Player.cs + + + 201609171250159_InitialCreate.cs + + + 201609172056012_AddRowVersion.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + H4sIAAAAAAAEAO1c227bNhi+H7B3EHS1DamVpDdbYLdInQOCNUkRJ8XuAkaiHWI6uBIVxCv2ZLvYI+0V9uvMsyTbcZymKNAmIvnxP/HnT/JD//vn3+H7x8C3HnCckCgc2XuDXdvCoRt5JJyN7JRO3/xqv3/34w/DYy94tD5X/d5m/WBkmIzse0rnB46TuPc4QMkgIG4cJdGUDtwocJAXOfu7u785e3sOBggbsCxreJWGlAQ4/wV+HUehi+c0Rf555GE/Kb9DyyRHtS5QgJM5cvHI/hCh2DuF3weHc2Jbhz5BIMQE+1PbQmEYUURBxIObBE9oHIWzyRw+IP96McfQb4r8BJeiHzTdu2qxu59p4TQDKyg3TWgU9ATce1uaxRGHL2VcuzYbGO4YDEwXmda58Ub24QxfAXY4sy1xtoOxH2c9BeMOCmcM6pE7Fte+U8cDhE32Z8capz5NYzwKcUpj5O9Yn9I7n7i/48V19CcOR2Hq+6yYICi0cR/g06c4muOYLq7wtBT+zLMthx/niAPrYcyYQqvTlMDPFzA3uvNxHQSOcXj2dwUAkZQb7hw9fsThjN6PbPjRtk7II/aqLyXqTUhg+cAgGqetk4wjP4rPQo+4iEbx+qe7QA9klvtYmDjzYWJbV9jPW5N7Mi8W0SBruWWC5SSOgqvIL8c0LbfXKJ5hCjJHyuZJlMauINHQacLSGKynufF7xmn2+/cQXXeIHnukyEhPPE8dOaJRzMOOcOLGZL4REWFPiZ/G5tp1yizEVdZqtRg1a7Vayl2lGiOKZ1FMTCmE7SPI1TSpBWPaVZJ1ziIlzqJ3JqkGfs8m38ASatnqusWpcrNTxPFycRpFMRTcgNY/UuuhrypWP4JtaOrVYXQUgSq4PwycD/rjdPbrJx8tcNzbp8WwV+XPDRXb1VpZUdrMJytCnAVohm9i/8mVzoIJkCckdJsoBxNck6DDetFXAEzKUmTWIoRv2V5NbpUapSpA7tG3QikOEHrJinZJpjyta6TJ2/rUI4dJErkkF4CRiqnXeF2OQ88yF29FpFQ7FwQLJAAyhyUPk4/sXyTraBHr3axBZKpIHnZ3MNiTkCFZ4BiH2cXKGMwL6YeEVM4sEHRkjnyjEMKojhkps3qNL7Yc4TkOPRDPaM8uE3OHElmCeiIhW7aZZ+gwsdEhZJgaxehhVWG9YtCoaqAGsqmwjbA99JUzh048QxppBKy24O5a63MPqzeT2HjoPTGuhpfhEfYxxdahW9wMjlHiIk9OtpBGvDUsM60CG1hpWp90mZvfn59ltbG7Q0uA8FvFGiKO219alu3WRBkr9Obii7V9l1mrgm0DMVWUAjCGwggclwLkpfxf2Vf8SBUHAjgWl2eCpCzrxGDJUCeYivsSHFqb4kPeyqWY42HKo7GEUIRcy2D24CwhNBtDG0q96JUwTJ5tASpCQwVSrUgBgHEgbxH2Ro3ppL5zEyOqtYqr5a7NL0Vla93GYLCRIK50XsOu2rN+1aivq0c6VST9DaCoQRgQVt6VLaA4uMg2aKlROlYpjAp17Bosoa9LWFuwq2ldxiiygdYM8mbZul0upzq3QbZFkUHd6rxWJ+nmudkp3purd2lH8zA9PEfzebbmmpHlF2tSvFKP30z6v+EGBYbjJoqn3FraeiYaxWiGhdYsBjx8QuKEwjEf3aHsVD/2AqkbvyVpcms1l7TryM6qkm41JPuZ2fzqSy5t6myMeAJ6Bdmmn6mIVclOHptzBZCPYsXF1Djy0yDUFyD60cW9FDv+QrE1mhDEF14WS2yTUYeOYBKpgJEML1V7vCs7ObpYUcv7WJUSOrhXPWxbPVs/jLIg9cfuONwdA4tlvHzQ43Evoiwe19Adr3myYcGar1sTtcaqpGvo1oVr//DVD93WEH45rmXqmhV8qy3YunjXMPhp/Ns8crEYzdceSM07FwfVfN4aT1f14PJeLo97/T2sG7itq5e/L+MLC9NNmh6xuidhsXR3Jwa71G9bnHXqr92R+McrFo1v2Xz08ocLuXriTtGmMonrqCqIdLtKdj5SuE9x4paN0ymc9ZdmzMz9hdJeFnYSqrJGP8HEw6DsUulMKHapA6o+GwpnwGF5HmtnMEsHtKKLbYHsD8TLDmeTRUJxMMg6DCZf/LFPIISbDucoJFOc0OKd397f3dsXmNDbw0p2ksTzFedZJTWZ99oG2AppSL6kmGQ3wGRKslvCFZgL4QOK3XsU/xSgx59ZpOWpwD0g+5FrX7GlBUbrSlgK1qqsqHTdD27GjyP7aw5yYJ39ccvg7FiXMazxA2vX+ruvOAo27ErqiXQ9AUxSLAutakypZP7PYdPF+tqmVn9+5ysO5r4eqmy2QS/pnvJfgp9EouHUjxBdnWfYBaYvzfDlGXeNe6fM7FsqDbNAujy8BEVwKVkKiJWkEFmGKxlZxST0wFK0nUnYqzRZJatrzywKton2JNG+BrbDu6wGq8R8DdNDmrVzHpk396VZifyz8RLct6WYMKYHrSehMr5M5qLyNqKvy9bKe+LnbhgrggwdOY7Lhk9nj64hcgyPG89KmmoPHtWtEeNApfN0jvsGg6iXYzcdSOY7u82xOjtwilnez+bpviLjZSle6XLxY3oZeyIW57fECX7OzWvTsbKpDatHlDw7v1fmXIne0vB3zfTd4mFgZN/lXC1wcVGPGzifKoavluCrgVfz6TT034WR/KuZwUDZ1PODW+jBuqkMjEg1hdjAINbMoeEwKp1hMBvfbHCOwXxPTWqWAlfg5bVxeSXi5vpZyzXLtJW8rHkg7SuyoQrUveWuX+mKF9uutPoBVllMq8hN26D82lnacpYROT/t/GQFrXlbONj9w3gdKvXgWctv57BFM/9HGBQHCZk1ENn/GBZil9uc6z5n4TSqagRBoqqLcJN2jinyYOc+jCmZIpdCs4uTJE9on5GfQpfj4A57Z+FlSucpBZVxcOdzoZzVGqb5czI5L/PwMn+pS9ahAohJstvYy/BDSnyvlvtEcXengciKmFMM3wtfQk0EC3ZRI11EYUeg0nx17XWNg7mf7cCX4QQ94GVku0nwRzxD7qKiQOhB2h3Bm314RNAsRkFSYjTj4VeIYS94fPc/8nbPBipPAAA= + + + boardz + + \ No newline at end of file diff --git a/src/BoardZApi/BoardGame.Api/Migrations/201609172056012_AddRowVersion.Designer.cs b/src/BoardZApi/BoardGame.Api/Migrations/201609172056012_AddRowVersion.Designer.cs new file mode 100644 index 0000000..8d687e7 --- /dev/null +++ b/src/BoardZApi/BoardGame.Api/Migrations/201609172056012_AddRowVersion.Designer.cs @@ -0,0 +1,29 @@ +// +namespace BoardGame.Api.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 AddRowVersion : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(AddRowVersion)); + + string IMigrationMetadata.Id + { + get { return "201609172056012_AddRowVersion"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/BoardZApi/BoardGame.Api/Migrations/201609172056012_AddRowVersion.cs b/src/BoardZApi/BoardGame.Api/Migrations/201609172056012_AddRowVersion.cs new file mode 100644 index 0000000..9ac8175 --- /dev/null +++ b/src/BoardZApi/BoardGame.Api/Migrations/201609172056012_AddRowVersion.cs @@ -0,0 +1,20 @@ +namespace BoardGame.Api.Migrations +{ + using System; + using System.Data.Entity.Migrations; + + public partial class AddRowVersion : DbMigration + { + public override void Up() + { + AddColumn("boardz.Games", "RowVersion", c => c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion")); + AddColumn("boardz.Categories", "RowVersion", c => c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion")); + } + + public override void Down() + { + DropColumn("boardz.Categories", "RowVersion"); + DropColumn("boardz.Games", "RowVersion"); + } + } +} diff --git a/src/BoardZApi/BoardGame.Api/Migrations/201609172056012_AddRowVersion.resx b/src/BoardZApi/BoardGame.Api/Migrations/201609172056012_AddRowVersion.resx new file mode 100644 index 0000000..aa54506 --- /dev/null +++ b/src/BoardZApi/BoardGame.Api/Migrations/201609172056012_AddRowVersion.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 + + + H4sIAAAAAAAEAO1c3W7bOBa+X2DfQdDV7iJjJZ2b2cCeQeokRbBNUsTpYO8KRqIdYiVKQ1GZeAb7ZHuxjzSvMKR++S/Jdly3KQq0iUh+PDzn8JxD8kP/+N//pz89J7H3BEmOUjzzTybHvgdxmEYIr2Z+QZff/eD/9ONf/zK9iJJn7+em3/e8HxuJ85n/SGl2GgR5+AgTkE8SFJI0T5d0EqZJAKI0eHN8/M/g5CSADMJnWJ43vSswRQksf2G/zlMcwowWIL5OIxjn9XfWsihRvRuQwDwDIZz5b1NAonfs98lZhnzvLEaACbGA8dL3AMYpBZSJePoxhwtKUrxaZOwDiO/XGWT9liDOYS36add96CqO3/BVBN3ABioscpomIwFPvq/VEqjDN1Ku36qNKe6CKZiu+apL5c38sxW8Y9h45XvqbKfzmPCeinInlTEm7cgjT2o/av2BuQ3/c+TNi5gWBM4wLCgB8ZH3oXiIUfgvuL5P/wPxDBdxLIrJBGVt0gf26QNJM0jo+g4ua+GvIt8L5HGBOrAdJoypVvWuQOznGzY3eIhh6wSBczj/uwFgnlQq7ho8v4d4RR9nPvvR9y7RM4yaLzXqR4zY9mGDKCl6J5mncUqucIRCQFOy++luwBNalTZWJuY2zH3vDsZla/6IsmoTTXjLJ8FZLkma3KVxPaZr+XQPyApSJnNqbF6kBQkViaZB55ZOZ31XKn+kn/Lfv7norl30IkJVRHrheVrPUZXiHnYO85CgbC8ispxC9qLzu/TX2m+bqd4iDMja93imLAhhKXp9XcKVc0ki/GAWQPMtYV8tWPCB7yCGBFAYfQCUQoJ5dEqygkLdBtawIsSNbUJLEzssoaWJPEOlmrNVrVKCXBFP7KPI1TWZBRPaTZINDno1znp04GsGfgt+33b8vnd8TyExbFsZSwnDtttsW6UpYccZhjZ+Y7VDX9XWes90Q4uo9frzlC3F5FA9MOz0NR5nsF0/xGANyWibVsNelT33dJRp9sqW0nKbbAlxlYAV/EjiF180dyaGvEA47LycqeAeJQP2i71gEUKWIbJWLvxJ7NXFVq1RK1r0HmMLqup4ZpesatdkKsO6RZqybUz5dJbnaYhKAQSphPJSXssFjjx3rVl5SpO5mLOwAIAytuXZ5DP/H5p2rIhtNusQhaJXhj2eTE40ZBYsIEv4/NqKZf+chR+EqR5ZmNOhDMROIZRRAyMS13qLr7acwwziiInn1OeQiaUjny5BO5ESLfvUMw0E3xjgMkKN4rSw6RywpdOYaqAOsjsQOGFHrFePHDbxHGGkE7BJwcNXbY894rqFwCZDn6h+Nb3F5zCGFHpnYXXvOgd5CCI92LIwEu1gm1kXsIedZrXJkLnl/PxZdpuYHXocRE4VO/A4Kb/0bNuD8TJR6P35l6j7IbM2BdsefKoqBdgYykZAUgtQlvK/8a/wmRoOBOwUX58J8rqsU52Foy4gVfMSO7R2xYeeyjWfk2Hqo7GGULlcz2Dx4KwhdImhD6Xd9EYYIc72AFWuYQJpdqQCIBhQ1oh4ASh0Ml8Rqh7VW8W1crfq17yyt24TMERPUHe6vMKhqxftalm+rR4ZVJGMV4ChBhFARHm31oDh4KLroKdGGVilCEtofdehCXtdIupC3E27UkYVDaxq0JNlb7rcbOlSguzzIsdym/NaG6S7x/yges1vXv0Dy7P/9BpkGd9z3cj6i7eoOADz7xbjX8iTCiMIc8NDeSttOxNNCVhBpZX7QAQvEckpO+aDB8BP9fMo0brJKckSW5u5tKyjG6sJus0Q/rOQ/NpLLmvo7JR4ydaV8KRf3gGbgp0+1uNMDBADYriYmqdxkWB7AWIfXd1LieNvDKnRhaC+n4tYapuOOg0UlWgFjKZ4rdqTTTnI0NWO2tzGppAwwLzmYYdq2fbZWQRpPw7Hke4YRCzn5YMdT3pvFvGkhuF43QuTCNZ9HY4kPiGJWOL3g9kDzhpn6EZoy+Dxm8E+9FA3xGt1FKHm2sJTrMXkEF9xDH4Zb+ke4ESM7usIpO4NToLqPh+MpZtadXMr10fR8Ra2DTzUWCDf5clFj+uWz47Y3OGIWLZ7HYde2nc3STvt1+FI8sOaiCa37N975YOPXtlJJ3xXCSd1NBVrthzFz24G8xluA3TlDHJn+4WeMPN4oawXmYOEarQxTjD1oKqbVDuvql1ah2rPrcr5dFqfFfu569rhserie0z2JxTxg+NinVOYTHiHyeKXeB4j5sJdh2uA0RLmtOIg+G+OT94oHPjD4aMHeR7FhrO2kZQuW20PTIoCo18KiPjtNFoifoO5BasCPwESPgLytwQ8/11E2pwEPgJyHK36FWta4TJvhWXgK+sL1Z4imJnh88z/vQQ59a7+/UnAOfJuCdvjp96x99+x4hh40FstT2U+KmDawrhrNWPqRZb/nHVdvN/HL0vnRZL016fmSy+LcTdMM3Mifl1bZ6w/NDr7an3CRqH4ErxCJXgu4xTQ7fmdQ2DG0ju/POXusC7QGZUbpRgRyJZjNqBmbiRLBbGVFCq7cyslmxicEdMU7Wdwjiq7tskh1vOYgeVjPSX174HDsK64gm18voUZIc3OuaYC12FjNqj8XL8B53AjBpLrIfFFKKRfJmPUeNMy1mQ75ZvJc3dMIUWGgdzSTd1nsEV34DmOZ6DPSlbrdx7TjZhgQKPxbIb7Cp1olGH37Uju+8j9sWkHcLlFvtX+adYq02gjPu9m/uN69Xsh9uzXxMX+nMlr376yr4Q1wks+O69a57qp1rLwpt206erRY+Y/lBw5ZuKqHndwbU3Maiux2gJv5jFaaNdrJ+naMoODKmvnZffQsm1TOZioZuq2g7ltmcPCHTUaw6E2udlhHIf6XppMrjmuwofs41BrhNnds8Vbdm8vadzy+DtWZEcVaHun3v2iGz5y/6LNj8vGYtpEAzuExe+cHa9HGZXP1M8LN9DJD4X7Pt6Nd7GkEfx2nRfAUrTwP9+x4iBHqw6C/z94GIZScm77XOFl2tQIikRNF+Um7RpSELHMfUYoWoKQsuYQ5nkZ0H4GccG6XCQPMLrCtwXNCsqWDJOHWHJlXmu45i9J/LLM09vyFTLfxRKYmIjfxt7itwWKo1buS8PdnQWCFzH1cxC3JeXPQqt1i3ST4oFAtfra2useJlnMM/AtXoAnuIlsH3P4Hq5AuG7oHXaQfkPIap+eI7AiIMlrjG48+5X5cJQ8//gnk4U8fwBSAAA= + + + boardz + + \ No newline at end of file diff --git a/src/BoardZApi/BoardGame.Api/Migrations/Configuration.cs b/src/BoardZApi/BoardGame.Api/Migrations/Configuration.cs new file mode 100644 index 0000000..f4c2f09 --- /dev/null +++ b/src/BoardZApi/BoardGame.Api/Migrations/Configuration.cs @@ -0,0 +1,32 @@ +namespace BoardGame.Api.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 = "BoardGame.Api.BoardzContext"; + } + + protected override void Seed(BoardGame.Api.BoardzContext 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/src/BoardZApi/BoardGame.Api/Models/Category.cs b/src/BoardZApi/BoardGame.Api/Models/Category.cs index 7228a10..d0f23bd 100644 --- a/src/BoardZApi/BoardGame.Api/Models/Category.cs +++ b/src/BoardZApi/BoardGame.Api/Models/Category.cs @@ -58,6 +58,11 @@ public IList GameNames /// /// NumberOfGames /// - public int NumberOfGames => this.GameNames.Count; + public int NumberOfGames => this.GameNames.Count; + + /// + /// RowVersion -> required for Offline Support + /// + public byte[] RowVersion { get; set; } } } diff --git a/src/BoardZApi/BoardGame.Api/Models/Game.cs b/src/BoardZApi/BoardGame.Api/Models/Game.cs index db14544..ab01cf2 100644 --- a/src/BoardZApi/BoardGame.Api/Models/Game.cs +++ b/src/BoardZApi/BoardGame.Api/Models/Game.cs @@ -55,5 +55,10 @@ public Game() /// [JsonIgnore] public string UserName { get; set; } + + /// + /// RowVersion -> required for Offline Support + /// + public byte[] RowVersion { get; set; } } } \ No newline at end of file diff --git a/src/BoardZApi/BoardGame.Api/app.config b/src/BoardZApi/BoardGame.Api/app.config index 160e67d..0608f03 100644 --- a/src/BoardZApi/BoardGame.Api/app.config +++ b/src/BoardZApi/BoardGame.Api/app.config @@ -1,5 +1,6 @@  +
@@ -45,5 +46,8 @@ - + + + + \ No newline at end of file diff --git a/src/BoardZApi/docs/BoardGame.Api.xml b/src/BoardZApi/docs/BoardGame.Api.xml index 2c005e2..d8894e9 100644 --- a/src/BoardZApi/docs/BoardGame.Api.xml +++ b/src/BoardZApi/docs/BoardGame.Api.xml @@ -358,6 +358,11 @@ Name of the user who created the game
+ + + RowVersion -> required for Offline Support + + Category model @@ -398,6 +403,11 @@ NumberOfGames + + + RowVersion -> required for Offline Support + + Coordinate DataType From ef5f7194713e40271a3cbad269207c30520ce203 Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Sat, 17 Sep 2016 23:01:12 +0200 Subject: [PATCH 26/53] take pics as jpeg not as png --- src/BoardZ/app/services/mobileCameraService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BoardZ/app/services/mobileCameraService.ts b/src/BoardZ/app/services/mobileCameraService.ts index 50aea5c..016497b 100644 --- a/src/BoardZ/app/services/mobileCameraService.ts +++ b/src/BoardZ/app/services/mobileCameraService.ts @@ -18,7 +18,7 @@ export class MobileCameraService implements ICameraService { quality: 50, destinationType: camera.DestinationType.DATA_URL, sourceType: camera.PictureSourceType.CAMERA, - encodingType: camera.EncodingType.PNG, + encodingType: camera.EncodingType.JPEG, saveToPhotoAlbum: false, correctOrientation: true }; From 194f0eaf9404e14fa562859951ef0e19c5a2195c Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Sat, 17 Sep 2016 23:31:16 +0200 Subject: [PATCH 27/53] Execute Migrations when starting SelfHost --- src/BoardZApi/BoardGame.Api/Migrations/Configuration.cs | 2 +- src/BoardZApi/BoardGame.Host/Program.cs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/BoardZApi/BoardGame.Api/Migrations/Configuration.cs b/src/BoardZApi/BoardGame.Api/Migrations/Configuration.cs index f4c2f09..f59916b 100644 --- a/src/BoardZApi/BoardGame.Api/Migrations/Configuration.cs +++ b/src/BoardZApi/BoardGame.Api/Migrations/Configuration.cs @@ -5,7 +5,7 @@ namespace BoardGame.Api.Migrations using System.Data.Entity.Migrations; using System.Linq; - internal sealed class Configuration : DbMigrationsConfiguration + public sealed class Configuration : DbMigrationsConfiguration { public Configuration() { diff --git a/src/BoardZApi/BoardGame.Host/Program.cs b/src/BoardZApi/BoardGame.Host/Program.cs index 2b03b0b..062f746 100644 --- a/src/BoardZApi/BoardGame.Host/Program.cs +++ b/src/BoardZApi/BoardGame.Host/Program.cs @@ -2,6 +2,8 @@ using BoardGame.Api; using System.Linq; using Microsoft.Owin.Hosting; +using System.Data.Entity; +using BoardGame.Api.Migrations; namespace BoardGame.Host { @@ -9,7 +11,7 @@ class Program { static void Main(string[] args) { - + Database.SetInitializer(new MigrateDatabaseToLatestVersion()); using (WebApp.Start("http://+:8080")) { Console.WriteLine("Server is up and running"); From c1b6273225f58dda5d12f1a418b3688c1a629a70 Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Sat, 17 Sep 2016 23:31:28 +0200 Subject: [PATCH 28/53] Added ConnectionState.Initializing --- src/BoardZ/app/models/connectionState.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/BoardZ/app/models/connectionState.ts b/src/BoardZ/app/models/connectionState.ts index c9d9bfa..38f2f0b 100644 --- a/src/BoardZ/app/models/connectionState.ts +++ b/src/BoardZ/app/models/connectionState.ts @@ -1,4 +1,5 @@ export enum ConnectionState{ + Initializing = -1, Offline = 0, ToSlow = 10, Normal = 20, From d63e8d05f503a94ab14acbfc1f9a6926a9b67243 Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Sat, 17 Sep 2016 23:31:56 +0200 Subject: [PATCH 29/53] Games.getAll respects the current ConnectionState (SPIKE) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit just tried if it works as expected… --- src/BoardZ/app/services/gamesService.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/BoardZ/app/services/gamesService.ts b/src/BoardZ/app/services/gamesService.ts index aa79ed8..155bb9d 100644 --- a/src/BoardZ/app/services/gamesService.ts +++ b/src/BoardZ/app/services/gamesService.ts @@ -3,10 +3,12 @@ import {Headers} from '@angular/http'; import {Observable} from 'rxjs/Observable'; import {AuthenticatedHttp} from './authenticatedHttp'; import {Game} from '../models/game'; +import {OfflineDetectionService} from './offlineDetectionService'; @Injectable() export class GamesService { - constructor(private _http: AuthenticatedHttp) { + constructor(private _http: AuthenticatedHttp, + private _offlineDetectionService: OfflineDetectionService) { } private getRequestOptions() { @@ -19,10 +21,12 @@ export class GamesService { return { headers: headers }; } - public getAll(): Observable { - return this._http.get('api/games/list') + public getAll(): Observable> { + + return Observable.if(()=>{ return this._offlineDetectionService.isOnline }, this._http.get('api/games/list') .map(response => response.json()) - .map(rawGames => rawGames.map(game => Game.fromRawJson(game))); + .map(rawGames => rawGames.map(game => Game.fromRawJson(game))),Observable.of([])); + } public deepClone(game: Game): Game { From 530af322abc0689655b2a6834b1f7d0ee1a1af0c Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Sat, 17 Sep 2016 23:32:44 +0200 Subject: [PATCH 30/53] Offline detection service can now emit an event when the device is restoring the connection closes #14 --- .../app/services/offlineDetectionService.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/BoardZ/app/services/offlineDetectionService.ts b/src/BoardZ/app/services/offlineDetectionService.ts index dcef1dd..04b6613 100644 --- a/src/BoardZ/app/services/offlineDetectionService.ts +++ b/src/BoardZ/app/services/offlineDetectionService.ts @@ -7,16 +7,20 @@ import {Observable} from '/rxjs/Observable'; import 'rxjs/add/operator/timeout'; import 'rxjs/add/operator/map'; import 'rxjs/add/operator/catch'; +import {Subject} from 'rxjs/Rx'; + @Injectable() export class OfflineDetectionService { - private _recentConnectionState: ConnectionState = ConnectionState.Unknown; + private _recentConnectionState: ConnectionState = ConnectionState.Initializing; private _monitoringHandle: number; constructor(private _http: Http, private _apiConfig: ApiConfig, private _offlineConfig: OfflineConfig) { - this._recentConnectionState = ConnectionState.Unknown; + this._recentConnectionState = ConnectionState.Initializing; } + public connectionRestoring: Subject = new Subject(); + private get pingUrl(): string { return `${this._apiConfig.rootUrl}api/status/ping`; } @@ -26,7 +30,7 @@ export class OfflineDetectionService { } public get isOnline(): boolean { - return this._recentConnectionState > ConnectionState.ToSlow; + return this._recentConnectionState > ConnectionState.ToSlow || this._recentConnectionState === ConnectionState.Initializing; } private checkConnection(): Observable { @@ -56,7 +60,12 @@ export class OfflineDetectionService { public startConnectionMonitoring(): void { this._monitoringHandle = setInterval(() => { - this.checkConnection().subscribe(state => this._recentConnectionState = state) + this.checkConnection().subscribe(state => { + if (this._recentConnectionState < ConnectionState.Normal && state > ConnectionState.ToSlow) { + this.connectionRestoring.next(state); + } + this._recentConnectionState = state; + }); }, this._offlineConfig.checkInterval); } From 3f663d3aa21f5b19801ad385c5980b78c44650f6 Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Sat, 17 Sep 2016 23:55:30 +0200 Subject: [PATCH 31/53] Started Sync Controller and implemented GetGamesSince --- .../BoardGame.Api/BoardGame.Api.csproj | 3 ++ src/BoardZApi/BoardGame.Api/BoardzContext.cs | 2 + .../Controllers/BaseApiController.cs | 25 +++++++++++ .../Controllers/CategoriesController.cs | 2 +- .../Controllers/GamesController.cs | 16 +++++++- .../Controllers/SyncController.cs | 41 +++++++++++++++++++ .../EntityFrameworkExtensions/Extensions.cs | 13 ++++++ .../BoardGame.Api/Models/Category.cs | 5 +++ src/BoardZApi/BoardGame.Api/Models/Game.cs | 6 +++ .../BoardGame.Api/Services/GameService.cs | 23 +++++++---- src/BoardZApi/docs/BoardGame.Api.xml | 36 +++++++++++++++- 11 files changed, 162 insertions(+), 10 deletions(-) create mode 100644 src/BoardZApi/BoardGame.Api/Controllers/BaseApiController.cs create mode 100644 src/BoardZApi/BoardGame.Api/Controllers/SyncController.cs create mode 100644 src/BoardZApi/BoardGame.Api/EntityFrameworkExtensions/Extensions.cs diff --git a/src/BoardZApi/BoardGame.Api/BoardGame.Api.csproj b/src/BoardZApi/BoardGame.Api/BoardGame.Api.csproj index c357082..0b04ba4 100644 --- a/src/BoardZApi/BoardGame.Api/BoardGame.Api.csproj +++ b/src/BoardZApi/BoardGame.Api/BoardGame.Api.csproj @@ -128,10 +128,13 @@ + + + diff --git a/src/BoardZApi/BoardGame.Api/BoardzContext.cs b/src/BoardZApi/BoardGame.Api/BoardzContext.cs index 62d3afc..0dbca19 100644 --- a/src/BoardZApi/BoardGame.Api/BoardzContext.cs +++ b/src/BoardZApi/BoardGame.Api/BoardzContext.cs @@ -103,7 +103,9 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) modelBuilder.Entity().Property(game => game.RowVersion).IsRowVersion(); + modelBuilder.Entity().Ignore(game => game.RowVersionAsInt); modelBuilder.Entity().Property(category => category.RowVersion).IsRowVersion(); + modelBuilder.Entity().Ignore(category => category.RowVersionAsInt); } } diff --git a/src/BoardZApi/BoardGame.Api/Controllers/BaseApiController.cs b/src/BoardZApi/BoardGame.Api/Controllers/BaseApiController.cs new file mode 100644 index 0000000..b2f5732 --- /dev/null +++ b/src/BoardZApi/BoardGame.Api/Controllers/BaseApiController.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web.Http; + +namespace BoardGame.Api.Controllers +{ + /// + /// Base ApiController class + /// + public class BaseApiController: ApiController + { + /// + /// Converts a string or null to a valid RowVersion + /// + /// + /// + protected byte[] GetRowVersion(string rowVersion = null) + { + return rowVersion != null ? Convert.FromBase64String(rowVersion) : null; + } + } +} diff --git a/src/BoardZApi/BoardGame.Api/Controllers/CategoriesController.cs b/src/BoardZApi/BoardGame.Api/Controllers/CategoriesController.cs index 465ead7..918a965 100644 --- a/src/BoardZApi/BoardGame.Api/Controllers/CategoriesController.cs +++ b/src/BoardZApi/BoardGame.Api/Controllers/CategoriesController.cs @@ -12,7 +12,7 @@ namespace BoardGame.Api.Controllers /// Categories Controller /// [Authorize] - public class CategoriesController: ApiController, IDisposable + public class CategoriesController: BaseApiController, IDisposable { private readonly CategoriesService _categoriesService; diff --git a/src/BoardZApi/BoardGame.Api/Controllers/GamesController.cs b/src/BoardZApi/BoardGame.Api/Controllers/GamesController.cs index 1a91961..a5c509a 100644 --- a/src/BoardZApi/BoardGame.Api/Controllers/GamesController.cs +++ b/src/BoardZApi/BoardGame.Api/Controllers/GamesController.cs @@ -12,7 +12,7 @@ namespace BoardGame.Api.Controllers /// Provides a CRUD api for board games ///
[Authorize] - public class GamesController : ApiController + public class GamesController : BaseApiController { private readonly GameService _gameService; @@ -24,6 +24,20 @@ public GamesController() _gameService = new GameService(); } + /// + /// Method for loading games since a given row version + /// + /// + /// + [HttpGet] + public IEnumerable Since(string rowVersion = null) + { + var rv = this.GetRowVersion(rowVersion); + var username = User.GetCurrentUsernameOrThrow(); + var games = _gameService.GetAll(username, rv); + + return games; + } /// /// Lists all games /// diff --git a/src/BoardZApi/BoardGame.Api/Controllers/SyncController.cs b/src/BoardZApi/BoardGame.Api/Controllers/SyncController.cs new file mode 100644 index 0000000..8cd7a7e --- /dev/null +++ b/src/BoardZApi/BoardGame.Api/Controllers/SyncController.cs @@ -0,0 +1,41 @@ +using BoardGame.Api.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System.Web.Http; + +namespace BoardGame.Api.Controllers +{ + public class SyncController : ApiController + { + /// + /// Sync games + /// + /// + /// + [HttpPost] + public HttpResponseMessage SyncGames([FromBody]IEnumerable games) + { + // will return latest rowVersion after sync + return Request.CreateResponse(HttpStatusCode.OK, 123123123); + } + + /// + /// sync all categories + /// + /// + /// + [HttpPost] + public HttpResponseMessage SyncCategories([FromBody]IEnumerable categories) + { + // will return latest rowVersion after sync + return Request.CreateResponse(HttpStatusCode.OK, 123123123); + } + + + } +} diff --git a/src/BoardZApi/BoardGame.Api/EntityFrameworkExtensions/Extensions.cs b/src/BoardZApi/BoardGame.Api/EntityFrameworkExtensions/Extensions.cs new file mode 100644 index 0000000..dacdbdd --- /dev/null +++ b/src/BoardZApi/BoardGame.Api/EntityFrameworkExtensions/Extensions.cs @@ -0,0 +1,13 @@ +using System; + + +namespace BoardGame.Api.EntityFrameworkExtensions +{ + public static class Extensions + { + public static int Compare(this byte[] b1, byte[] b2) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/BoardZApi/BoardGame.Api/Models/Category.cs b/src/BoardZApi/BoardGame.Api/Models/Category.cs index d0f23bd..45f8ed7 100644 --- a/src/BoardZApi/BoardGame.Api/Models/Category.cs +++ b/src/BoardZApi/BoardGame.Api/Models/Category.cs @@ -64,5 +64,10 @@ public IList GameNames /// RowVersion -> required for Offline Support ///
public byte[] RowVersion { get; set; } + + /// + /// the version that goes to the client + /// + public ulong RowVersionAsInt => BitConverter.ToUInt64(RowVersion.Reverse().ToArray(), 0); } } diff --git a/src/BoardZApi/BoardGame.Api/Models/Game.cs b/src/BoardZApi/BoardGame.Api/Models/Game.cs index ab01cf2..b18e1d0 100644 --- a/src/BoardZApi/BoardGame.Api/Models/Game.cs +++ b/src/BoardZApi/BoardGame.Api/Models/Game.cs @@ -1,6 +1,7 @@ using System; using Newtonsoft.Json; using System.Collections.Generic; +using System.Linq; namespace BoardGame.Api.Models { @@ -60,5 +61,10 @@ public Game() /// RowVersion -> required for Offline Support /// public byte[] RowVersion { get; set; } + + /// + /// the version that goes to the client + /// + public ulong RowVersionAsInt => BitConverter.ToUInt64(RowVersion.Reverse().ToArray(), 0); } } \ No newline at end of file diff --git a/src/BoardZApi/BoardGame.Api/Services/GameService.cs b/src/BoardZApi/BoardGame.Api/Services/GameService.cs index 4223dfb..486c4a0 100644 --- a/src/BoardZApi/BoardGame.Api/Services/GameService.cs +++ b/src/BoardZApi/BoardGame.Api/Services/GameService.cs @@ -1,4 +1,5 @@ -using BoardGame.Api.Models; +using BoardGame.Api.EntityFrameworkExtensions; +using BoardGame.Api.Models; using System; using System.Collections.Generic; using System.Data.Entity; @@ -25,10 +26,18 @@ public GameService() /// Returns all games for a given user /// /// + /// /// - public IEnumerable GetAll(string userName) - { + public IEnumerable GetAll(string userName, byte[] rowVersion = null) + { + if (rowVersion != null) + { return _dbContext.Games + .Include(game => game.AgeRating) + .Include(game => game.Categories) + .Where(c => c.RowVersion.Compare(rowVersion) > 0).ToList(); + } + return _dbContext.Games .Include(game => game.AgeRating) .Include(game => game.Categories) .Where(game => game.UserName.Equals(userName, StringComparison.InvariantCultureIgnoreCase)).ToList(); @@ -55,7 +64,7 @@ public Game GetById(Guid gameId, String userName) /// internal int Count(string username) { - return this.GetAll(username).Count(); + return this.GetAll(username, null).Count(); } /// @@ -70,7 +79,7 @@ public bool Update(Game game, String userName) { game.UserName = userName; var dbEntity = this.GetById(game.Id, userName); - if(dbEntity == null) + if (dbEntity == null) { return false; } @@ -98,7 +107,7 @@ public bool Update(Game game, String userName) public bool DeleteGame(Guid gameId) { var found = _dbContext.Games.FirstOrDefault(game => game.Id.Equals(gameId)); - if(found == null) + if (found == null) { return false; } @@ -133,7 +142,7 @@ public Guid AddGame(Game game, String userName) /// public void Dispose() { - if(_dbContext != null) + if (_dbContext != null) { _dbContext.Dispose(); } diff --git a/src/BoardZApi/docs/BoardGame.Api.xml b/src/BoardZApi/docs/BoardGame.Api.xml index d8894e9..0c34965 100644 --- a/src/BoardZApi/docs/BoardGame.Api.xml +++ b/src/BoardZApi/docs/BoardGame.Api.xml @@ -83,6 +83,18 @@ + + + Base ApiController class + + + + + Converts a string or null to a valid RowVersion + + + + Categories Controller @@ -149,6 +161,13 @@ default CTOR + + + Method for loading games since a given row version + + + + Lists all games @@ -270,6 +289,20 @@ Ping endpoint - Just returning a HTTP StatusCode 200 + + + Sync games + + + + + + + sync all categories + + + + Distance Calculator Logic @@ -553,11 +586,12 @@ Default CTOR - + Returns all games for a given user + From c60da7e826b4d71ee5918f54db5b2189be025f7d Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Sun, 18 Sep 2016 20:02:30 +0200 Subject: [PATCH 32/53] Dont include byte[] RowVersion in JSON / deal with rexisting categories for new games --- .../BoardGame.Api/Models/Category.cs | 18 ++++++++++++++- src/BoardZApi/BoardGame.Api/Models/Game.cs | 22 ++++++++++++++++--- .../BoardGame.Api/Services/GameService.cs | 8 +++++++ src/BoardZApi/docs/BoardGame.Api.xml | 12 +++++++++- 4 files changed, 55 insertions(+), 5 deletions(-) diff --git a/src/BoardZApi/BoardGame.Api/Models/Category.cs b/src/BoardZApi/BoardGame.Api/Models/Category.cs index d0f23bd..0eec1ef 100644 --- a/src/BoardZApi/BoardGame.Api/Models/Category.cs +++ b/src/BoardZApi/BoardGame.Api/Models/Category.cs @@ -61,8 +61,24 @@ public IList GameNames public int NumberOfGames => this.GameNames.Count; /// - /// RowVersion -> required for Offline Support + /// Category Row Version /// + [JsonIgnore] public byte[] RowVersion { get; set; } + + /// + /// the version that goes to the client + /// + public ulong RowVersionAsInt + { + get + { + if (RowVersion != null) + { + return BitConverter.ToUInt64(RowVersion.Reverse().ToArray(), 0); + } + return 0; + } + } } } diff --git a/src/BoardZApi/BoardGame.Api/Models/Game.cs b/src/BoardZApi/BoardGame.Api/Models/Game.cs index ab01cf2..718d2c7 100644 --- a/src/BoardZApi/BoardGame.Api/Models/Game.cs +++ b/src/BoardZApi/BoardGame.Api/Models/Game.cs @@ -1,7 +1,7 @@ using System; using Newtonsoft.Json; using System.Collections.Generic; - +using System.Linq; namespace BoardGame.Api.Models { /// @@ -14,7 +14,7 @@ public class Game /// public Game() { - + } /// /// Unique identifier @@ -24,7 +24,7 @@ public Game() /// /// Name of the board game /// - public string Name { get; set; } + public string Name { get; set; } /// /// Edition of the Game @@ -59,6 +59,22 @@ public Game() /// /// RowVersion -> required for Offline Support /// + [JsonIgnore] public byte[] RowVersion { get; set; } + + /// + /// the version that goes to the client + /// + public ulong RowVersionAsInt + { + get + { + if (RowVersion != null) + { + return BitConverter.ToUInt64(RowVersion.Reverse().ToArray(), 0); + } + return 0; + } + } } } \ No newline at end of file diff --git a/src/BoardZApi/BoardGame.Api/Services/GameService.cs b/src/BoardZApi/BoardGame.Api/Services/GameService.cs index 4223dfb..5c229f2 100644 --- a/src/BoardZApi/BoardGame.Api/Services/GameService.cs +++ b/src/BoardZApi/BoardGame.Api/Services/GameService.cs @@ -124,6 +124,14 @@ public Guid AddGame(Game game, String userName) game.Id = Guid.NewGuid(); game.UserName = userName; _dbContext.Games.Add(game); + foreach (var item in game.Categories) + { + _dbContext.Entry(item).State = EntityState.Unchanged; + } + if(game.AgeRating != null) + { + _dbContext.Entry(game.AgeRating).State = EntityState.Unchanged; + } _dbContext.SaveChanges(); return game.Id; } diff --git a/src/BoardZApi/docs/BoardGame.Api.xml b/src/BoardZApi/docs/BoardGame.Api.xml index d8894e9..8587149 100644 --- a/src/BoardZApi/docs/BoardGame.Api.xml +++ b/src/BoardZApi/docs/BoardGame.Api.xml @@ -363,6 +363,11 @@ RowVersion -> required for Offline Support + + + the version that goes to the client + + Category model @@ -405,7 +410,12 @@ - RowVersion -> required for Offline Support + Category Row Version + + + + + the version that goes to the client From f61737311e814b2fe81d4e389502d76f257265ef Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Sun, 18 Sep 2016 20:02:57 +0200 Subject: [PATCH 33/53] implemented BaseApiService which respects current ConnectionState --- src/BoardZ/app/components/categories/list.ts | 2 +- src/BoardZ/app/components/games/details.ts | 31 ++---- src/BoardZ/app/components/games/list.ts | 2 +- .../app/interfaces/supportsOfflineStorage.ts | 7 ++ src/BoardZ/app/models/ageRating.ts | 29 +++-- src/BoardZ/app/models/category.ts | 21 ++-- src/BoardZ/app/models/game.ts | 27 ++--- src/BoardZ/app/modules/config.ts | 4 + .../app/resolvers/categoryDetailsResolver.ts | 2 +- .../app/resolvers/gameDetailsResolver.ts | 2 +- src/BoardZ/app/services/ageRatingsService.ts | 39 +++---- src/BoardZ/app/services/authenticatedHttp.ts | 63 ++++------- src/BoardZ/app/services/baseApiService.ts | 100 ++++++++++++++++++ src/BoardZ/app/services/categoriesService.ts | 44 +++----- src/BoardZ/app/services/dashboardService.ts | 11 -- src/BoardZ/app/services/gamesService.ts | 46 +++----- src/BoardZ/app/services/logService.ts | 2 +- .../app/services/offlineStorageService.ts | 37 +++++++ src/BoardZ/app/services/playersService.ts | 23 +--- 19 files changed, 271 insertions(+), 221 deletions(-) create mode 100644 src/BoardZ/app/interfaces/supportsOfflineStorage.ts create mode 100644 src/BoardZ/app/services/baseApiService.ts create mode 100644 src/BoardZ/app/services/offlineStorageService.ts diff --git a/src/BoardZ/app/components/categories/list.ts b/src/BoardZ/app/components/categories/list.ts index f850a3b..a58fed3 100644 --- a/src/BoardZ/app/components/categories/list.ts +++ b/src/BoardZ/app/components/categories/list.ts @@ -28,7 +28,7 @@ export class CategoryListComponent implements OnInit { } public ngOnInit(): void { - this._categoriesService.getAll() + this._categoriesService.getAllCategories() .subscribe( (categories) => this.categories = categories, (err) => this._notificationService.notifyError('Error while fetching category data') diff --git a/src/BoardZ/app/components/games/details.ts b/src/BoardZ/app/components/games/details.ts index a6378fe..d4dc6c1 100644 --- a/src/BoardZ/app/components/games/details.ts +++ b/src/BoardZ/app/components/games/details.ts @@ -47,8 +47,9 @@ export class GameDetailsComponent implements OnInit { } public ngOnInit(): any { - this._categoriesService.getAll().subscribe(cats=>this.categories = cats); - this.ageRatings = this._ageRatingsService.getAll(); + this._categoriesService.getAllCategories().subscribe(cats=>this.categories = cats); + this._ageRatingsService.getAllAgeRatings().subscribe(ars=>this.ageRatings = ars); + this.route.data.forEach((data: { game: Game }) => { this.originalModel = this._gameService.deepClone(this.model = data.game || new Game()); this.selectedCategories = this.originalModel.categories.map(c=>c.id); @@ -58,22 +59,6 @@ export class GameDetailsComponent implements OnInit { }); } - private loadGame(id: string): void { - this._gameService.getById(id) - .subscribe( - (game) => { - this.originalModel = this._gameService.deepClone(this.model = game); - if (this._needsReset) { - this.reset(); - } - }, - (error) => { - this._logService.logError('Could not find game. Error was: ' + error); - this._notificationService.notifyError('Could not load game data.'); - } - ); - } - public abort(): void { this._router.navigate(['/games/all']); } @@ -89,10 +74,9 @@ export class GameDetailsComponent implements OnInit { setTimeout(() => this.active = true, 0); } - public saveChanges(): void { - this.model.categories = this.categories.filter(cat => this.selectedCategories.indexOf(cat.id) >-1); + this.model.categories = this.categories.filter(cat => this.selectedCategories.indexOf(cat.id) > -1); if (this.model.ageRatingId) { let found = this.ageRatings.filter(ar=>ar.id === this.model.ageRatingId); @@ -107,16 +91,15 @@ export class GameDetailsComponent implements OnInit { (newId) => { this._notificationService.notifySuccess('New game was added.'); this._needsReset = true; - this.loadGame(newId); + this.model.id = this.originalModel.id = newId; }, () => this._notificationService.notifyError('Could not save new game.') ); } else { this._gameService.updateGame(this.model) - .subscribe((oldId) => { + .subscribe(() => { this._notificationService.notifySuccess('Game data was updated.'); this._needsReset = true; - this.loadGame(oldId); }, () => { this._notificationService.notifyError('Could not update game data.'); @@ -127,7 +110,7 @@ export class GameDetailsComponent implements OnInit { public deleteGame(): void { if (window.confirm('Really delete the game "' + this.originalModel.name + '" ?')) { - this._gameService.deleteGame(this.originalModel.id) + this._gameService.deleteGame(this.originalModel) .subscribe( () => { this._notificationService.notifySuccess('Game data was deleted.'); diff --git a/src/BoardZ/app/components/games/list.ts b/src/BoardZ/app/components/games/list.ts index fa262d3..3d570d2 100644 --- a/src/BoardZ/app/components/games/list.ts +++ b/src/BoardZ/app/components/games/list.ts @@ -27,7 +27,7 @@ export class GameListComponent implements OnInit { } public ngOnInit(): void { - this._gamesService.getAll() + this._gamesService.getAllGames() .subscribe( (games) => this.games = games, (err) => this._notificationService.notifyError('Error while fetching game data') diff --git a/src/BoardZ/app/interfaces/supportsOfflineStorage.ts b/src/BoardZ/app/interfaces/supportsOfflineStorage.ts new file mode 100644 index 0000000..e2fd27c --- /dev/null +++ b/src/BoardZ/app/interfaces/supportsOfflineStorage.ts @@ -0,0 +1,7 @@ +import {ModelState} from '../models/modelState'; + +export interface ISupportsOfflineStorage { + id: string; + state: ModelState; + fromRawJson(rawJson: any): T; +} diff --git a/src/BoardZ/app/models/ageRating.ts b/src/BoardZ/app/models/ageRating.ts index 4eb7d1b..5009f74 100644 --- a/src/BoardZ/app/models/ageRating.ts +++ b/src/BoardZ/app/models/ageRating.ts @@ -1,17 +1,24 @@ -export class AgeRating{ +import {ISupportsOfflineStorage} from '../interfaces/supportsOfflineStorage'; +import {ModelState} from './modelState'; + +export class AgeRating implements ISupportsOfflineStorage { + + constructor() { + this.state = ModelState.Clean; + } + public id: string; - public name:string; + public name: string; public colorIndicator: string; + public state: ModelState; - - public static fromRawJson(rawJson: any): AgeRating{ - if(!rawJson){ - return new AgeRating(); + public fromRawJson(rawJson: any): AgeRating { + if (!rawJson) { + return this; } - let instance: AgeRating = new AgeRating(); - instance.id = rawJson.id || null; - instance.name = rawJson.name || null; - instance.colorIndicator = rawJson.colorIndicator || null; - return instance; + this.id = rawJson.id || null; + this.name = rawJson.name || null; + this.colorIndicator = rawJson.colorIndicator || null; + return this; } } diff --git a/src/BoardZ/app/models/category.ts b/src/BoardZ/app/models/category.ts index 4ad6a22..b5ae9fa 100644 --- a/src/BoardZ/app/models/category.ts +++ b/src/BoardZ/app/models/category.ts @@ -1,6 +1,7 @@ import {ModelState} from './modelState'; +import {ISupportsOfflineStorage} from '../interfaces/supportsOfflineStorage'; -export class Category { +export class Category implements ISupportsOfflineStorage { constructor() { this.state = ModelState.Clean; @@ -12,17 +13,15 @@ export class Category { public numberOfGames: number; public rowVersion: number; - - - public static fromRawJson(rawJson: any): Category { + public fromRawJson(rawJson: any): Category { if (!rawJson) { - return new Category(); + return this; } - let instance: Category = new Category(); - instance.id = rawJson.id || null; - instance.name = rawJson.name || null; - instance.rowVersion = rawJson.rowVersion || -1; - instance.numberOfGames = rawJson.numberOfGames || 0; - return instance; + + this.id = rawJson.id || null; + this.name = rawJson.name || null; + this.rowVersion = rawJson.rowVersion || -1; + this.numberOfGames = rawJson.numberOfGames || 0; + return this; } } diff --git a/src/BoardZ/app/models/game.ts b/src/BoardZ/app/models/game.ts index c73b47f..8a4f055 100644 --- a/src/BoardZ/app/models/game.ts +++ b/src/BoardZ/app/models/game.ts @@ -1,7 +1,8 @@ import {ModelState} from './modelState'; import {AgeRating} from './ageRating'; import {Category} from './category'; -export class Game { +import {ISupportsOfflineStorage} from '../interfaces/supportsOfflineStorage'; +export class Game implements ISupportsOfflineStorage { constructor() { this.state = ModelState.Clean; @@ -19,21 +20,21 @@ export class Game { public categories: Array; - public static fromRawJson(rawJson: any): Game { + public fromRawJson(rawJson: any): Game { if (!rawJson) { - return new Game(); + return this; } - let instance: Game = new Game(); - instance.id = rawJson.id || null; - instance.name = rawJson.name || null; - instance.ageRating = AgeRating.fromRawJson(rawJson.ageRating); - instance.ageRatingId = instance.ageRating.id || rawJson.ageRatingId; + + this.id = rawJson.id || null; + this.name = rawJson.name || null; + this.ageRating = (new AgeRating()).fromRawJson(rawJson.ageRating); + this.ageRatingId = this.ageRating.id || rawJson.ageRatingId; if(rawJson.categories){ - instance.categories = rawJson.categories.map(rawCategory=> Category.fromRawJson(rawCategory)); + this.categories = rawJson.categories.map(rawCategory=> (new Category).fromRawJson(rawCategory)); } - instance.description = rawJson.description || null; - instance.userName = rawJson.userName || null; - instance.rowVersion = rawJson.rowVersion || null; - return instance; + this.description = rawJson.description || null; + this.userName = rawJson.userName || null; + this.rowVersion = rawJson.rowVersion || null; + return this; } } diff --git a/src/BoardZ/app/modules/config.ts b/src/BoardZ/app/modules/config.ts index 3c9e277..0580339 100644 --- a/src/BoardZ/app/modules/config.ts +++ b/src/BoardZ/app/modules/config.ts @@ -44,6 +44,9 @@ import {CategoryListComponent} from '../components/categories/list'; import {GameDetailsComponent} from '../components/games/details'; import {CategoryDetailsComponent} from '../components/categories/details'; import {CategoryDetailsResolver} from '../resolvers/categoryDetailsResolver'; +import {OfflineStorageService} from '../services/offlineStorageService'; +import {Game} from '../models/game'; +import {Category} from '../models/category'; export namespace ModuleConfiguration { @@ -76,6 +79,7 @@ export namespace ModuleConfiguration { ApiConfig, OfflineConfig, OfflineDetectionService, + OfflineStorageService, NativeIntegrationService, AuthenticatedHttp, TokenService, diff --git a/src/BoardZ/app/resolvers/categoryDetailsResolver.ts b/src/BoardZ/app/resolvers/categoryDetailsResolver.ts index 7040ab7..366bcb6 100644 --- a/src/BoardZ/app/resolvers/categoryDetailsResolver.ts +++ b/src/BoardZ/app/resolvers/categoryDetailsResolver.ts @@ -11,7 +11,7 @@ export class CategoryDetailsResolver implements Resolve { resolve(route: ActivatedRouteSnapshot): Promise { let id = route.params['id']; return new Promise((resolve) => { - this._categoriesService.getById(id).subscribe(category => { + this._categoriesService.getCategoryById(id).subscribe(category => { if (category) { resolve(category); } else { diff --git a/src/BoardZ/app/resolvers/gameDetailsResolver.ts b/src/BoardZ/app/resolvers/gameDetailsResolver.ts index 472bbfa..738db2e 100644 --- a/src/BoardZ/app/resolvers/gameDetailsResolver.ts +++ b/src/BoardZ/app/resolvers/gameDetailsResolver.ts @@ -11,7 +11,7 @@ export class GameDetailsResolver implements Resolve { resolve(route: ActivatedRouteSnapshot): Promise { let id = route.params['id']; return new Promise((resolve) => { - this._gamesService.getById(id).subscribe(game => { + this._gamesService.getGameById(id).subscribe(game => { if (game) { resolve(game); } else { diff --git a/src/BoardZ/app/services/ageRatingsService.ts b/src/BoardZ/app/services/ageRatingsService.ts index 8d1bad1..a3b7b6b 100644 --- a/src/BoardZ/app/services/ageRatingsService.ts +++ b/src/BoardZ/app/services/ageRatingsService.ts @@ -1,39 +1,26 @@ import {Injectable} from '@angular/core'; -import {Headers} from '@angular/http'; import {AuthenticatedHttp} from './authenticatedHttp'; import {AgeRating} from '../models/ageRating'; +import {OfflineStorageService} from './offlineStorageService'; +import {OfflineDetectionService} from './offlineDetectionService'; +import {BaseApiService} from './baseApiService'; +import {Observable} from 'rxjs/Rx'; @Injectable() -export class AgeRatingsService { +export class AgeRatingsService extends BaseApiService { - constructor(private _http: AuthenticatedHttp) { - } - - private get _storageKey(): string { - return '_AGE_RATINGS'; - } - - private getRequestOptions() { - let headers = new Headers(); - headers.append('Accept', 'application/json'); - headers.append('Accept', 'text/plain'); - headers.append('Accept', '*/*'); - headers.append('Content-Type', 'application/json;charset=UTF-8'); - - return { headers: headers }; + constructor(http: AuthenticatedHttp, offlineStorageService: OfflineStorageService, offlineDetectionService: OfflineDetectionService) { + super(http, offlineStorageService, offlineDetectionService); + super.initializeEntity(AgeRating); } public initialize(): void { - this._http.get('api/ageratings/list') - .map(response => (response.json())) - .subscribe((ratings) => this.store(ratings)); - } - - private store(ageRatings: Array) { - localStorage.setItem(this._storageKey, JSON.stringify(ageRatings)); + this.getAllAgeRatings() + .subscribe(() => { + }); } - public getAll(): Array{ - return JSON.parse(localStorage.getItem(this._storageKey) || '[]').map(rawRating=>AgeRating.fromRawJson(rawRating)); + public getAllAgeRatings(): Observable> { + return this.getAll('api/ageratings/list'); } } diff --git a/src/BoardZ/app/services/authenticatedHttp.ts b/src/BoardZ/app/services/authenticatedHttp.ts index b476cd5..ca444cd 100644 --- a/src/BoardZ/app/services/authenticatedHttp.ts +++ b/src/BoardZ/app/services/authenticatedHttp.ts @@ -1,5 +1,5 @@ import {Injectable} from '@angular/core'; -import {Http, RequestOptionsArgs, Response, Headers} from '@angular/http'; +import {Http, Response, Headers, RequestOptions} from '@angular/http'; import {Observable} from 'rxjs/Observable'; import {ApiConfig} from '../apiConfig'; import {TokenService} from './tokenService'; @@ -15,60 +15,43 @@ export class AuthenticatedHttp { return `${this._config.rootUrl}${appendix}`; } - request(url: string, options?: RequestOptionsArgs): Observable { - url = this.buildUrl(url); - options = this.prepareOptions(options); - return this._http.request(url, options); - } + private getRequestOptions(): RequestOptions { + let requestOptions = new RequestOptions(); + requestOptions.headers = new Headers(); - get(url: string, options?: RequestOptionsArgs): Observable { - url = this.buildUrl(url); - options = this.prepareOptions(options); - return this._http.get(url, options); + requestOptions.headers.append('Accept', 'application/json'); + requestOptions.headers.append('Accept', 'text/plain'); + requestOptions.headers.append('Accept', '*/*'); + requestOptions.headers.append('Content-Type', 'application/json;charset=UTF-8'); + let token = this._tokenService.token; + if (token) { + requestOptions.headers.append('Authorization', 'Bearer ' + token); + } + return requestOptions; } - post(url: string, body: string, options?: RequestOptionsArgs): Observable { + public request(url: string): Observable { url = this.buildUrl(url); - options = this.prepareOptions(options); - return this._http.post(url, body, options); + return this._http.request(url, this.getRequestOptions()); } - put(url: string, body: string, options?: RequestOptionsArgs): Observable { + public get(url: string): Observable { url = this.buildUrl(url); - options = this.prepareOptions(options); - return this._http.put(url, body, options); + return this._http.get(url, this.getRequestOptions()); } - delete(url: string, options?: RequestOptionsArgs): Observable { + public post(url: string, body: string): Observable { url = this.buildUrl(url); - options = this.prepareOptions(options); - return this._http.delete(url, options); + return this._http.post(url, body, this.getRequestOptions()); } - patch(url: string, body: string, options?: RequestOptionsArgs): Observable { + public put(url: string, body: string): Observable { url = this.buildUrl(url); - options = this.prepareOptions(options); - return this._http.patch(url, body, options); + return this._http.put(url, body, this.getRequestOptions()); } - head(url: string, options?: RequestOptionsArgs): Observable { + public delete(url: string): Observable { url = this.buildUrl(url); - options = this.prepareOptions(options); - return this._http.head(url, options); - } - - protected prepareOptions(options: RequestOptionsArgs): RequestOptionsArgs { - let token = this._tokenService.token; - - if (token) { - options = options || {}; - - if (!options.headers) { - options.headers = new Headers(); - } - options.headers.append('Authorization', 'Bearer ' + token); - } - - return options; + return this._http.delete(url, this.getRequestOptions()); } } diff --git a/src/BoardZ/app/services/baseApiService.ts b/src/BoardZ/app/services/baseApiService.ts new file mode 100644 index 0000000..0ac981c --- /dev/null +++ b/src/BoardZ/app/services/baseApiService.ts @@ -0,0 +1,100 @@ +import {Injectable, Type} from '@angular/core'; +import {AuthenticatedHttp} from './authenticatedHttp'; +import {Observable} from 'rxjs/Rx'; +import {OfflineDetectionService} from './offlineDetectionService'; +import {OfflineStorageService} from './offlineStorageService'; +import {ISupportsOfflineStorage} from '../interfaces/supportsOfflineStorage'; + +@Injectable() +export class BaseApiService { + + private _entityType: Type; + constructor(private _authenticatedHttp: AuthenticatedHttp, + private _offlineStorageService: OfflineStorageService, + private _offlineDetectionService: OfflineDetectionService) { + + + } + + public initializeEntity(type: Type){ + this._entityType = type; + } + + /** + * return all items of the generic type + */ + protected getAll(url: string): Observable> { + + let httpObservable: Observable> = this._authenticatedHttp.get(url) + .map(response => response.json()) + .map(rawJsonResults => rawJsonResults.map(rawJsonResult => (new this._entityType()).fromRawJson(rawJsonResult))); + + let offlineObservable: Observable> = Observable.fromPromise(this._offlineStorageService.getAll()); + return Observable.if(()=> { + return this._offlineDetectionService.isOnline; + }, httpObservable, offlineObservable); + } + + /** + * return either an item by it's identifier or null + * @param id + */ + protected getSingle(id: string, url: string): Observable { + let httpObservable = this._authenticatedHttp.get(url) + .map(response => response.json()) + .map(rawJsonResult => (new this._entityType()).fromRawJson(rawJsonResult)); + let offlineObservable = Observable.fromPromise(>this._offlineStorageService.getById(id)); + + return >Observable.if(()=> { + return this._offlineDetectionService.isOnline; + }, httpObservable, offlineObservable); + + } + + /** + * persist the new item + * @param item + */ + public add(item: T, url: string): Observable { + let httpObservable = this._authenticatedHttp.post(url, JSON.stringify(item)) + .map(response => response.text()); + + let offlineObservable = Observable.fromPromise(this._offlineStorageService.add(item)); + + return Observable.if(()=> { + return this._offlineDetectionService.isOnline; + }, httpObservable, offlineObservable); + } + + /** + * Send an update for the item + * @param item + */ + public update(item: T, url: string): Observable { + let httpObservable = this._authenticatedHttp.put(url, JSON.stringify(item)) + .map(response => response.ok); + + let offlineObservable = Observable.fromPromise(this._offlineStorageService.update(item)); + + return Observable.if(()=> { + return this._offlineDetectionService.isOnline; + }, httpObservable, offlineObservable); + } + + /** + * Delete a given item, resolves with false if operation failed or item has no property id + * @param item + * @returns {any} + */ + public deleteItem(item: T, url: string): Observable { + let httpObservable = this._authenticatedHttp.delete(url) + .map(response => response.status); + + let offlineObservable = Observable.fromPromise(this._offlineStorageService.deleteItem(item)); + return Observable.if(()=> { + return this._offlineDetectionService.isOnline; + }, httpObservable, offlineObservable); + return Observable.of(false); + } + +} diff --git a/src/BoardZ/app/services/categoriesService.ts b/src/BoardZ/app/services/categoriesService.ts index 100fdb2..d50e0f7 100644 --- a/src/BoardZ/app/services/categoriesService.ts +++ b/src/BoardZ/app/services/categoriesService.ts @@ -1,51 +1,39 @@ import {Injectable} from '@angular/core'; -import {Headers} from '@angular/http'; import {Observable} from 'rxjs/Observable'; import {AuthenticatedHttp} from './authenticatedHttp'; import {Category} from '../models/category'; +import {BaseApiService} from './baseApiService'; +import {OfflineStorageService} from './offlineStorageService'; +import {OfflineDetectionService} from './offlineDetectionService'; @Injectable() -export class CategoriesService { - constructor(private _http: AuthenticatedHttp) { +export class CategoriesService extends BaseApiService { + constructor(http: AuthenticatedHttp, offlineStorageService: OfflineStorageService, offlineDetectionService: OfflineDetectionService) { + super(http, offlineStorageService, offlineDetectionService); + super.initializeEntity(Category); } - private getRequestOptions() { - let headers = new Headers(); - headers.append('Accept', 'application/json'); - headers.append('Accept', 'text/plain'); - headers.append('Accept', '*/*'); - headers.append('Content-Type', 'application/json;charset=UTF-8'); - - return { headers: headers }; - } - - public getAll(): Observable { - return this._http.get('api/categories/list') - .map(response => (response.json())) - .map(rawCategories => rawCategories.map(rawCategory => Category.fromRawJson(rawCategory))); + public getAllCategories(): Observable> { + return this.getAll('api/categories/list'); } public deepClone(category: Category): Category { return JSON.parse(JSON.stringify(category)); } - public getById(id: string): Observable { - return this._http.get(`api/categories/single?id=${id}`) - .map(response => response.json()); + public getCategoryById(id: string): Observable { + return this.getSingle(id, `api/categories/single?id=${id}`); } public addCategory(category: Category): Observable { - return this._http.post(`api/categories/add`, JSON.stringify(category), this.getRequestOptions()) - .map(response => response.json()); + return this.add(category, `api/categories/add`); } - public updateCategory(category: Category): Observable { - return this._http.put(`api/categories/update`, JSON.stringify(category), this.getRequestOptions()) - .map(response => category.id); + public updateCategory(category: Category): Observable { + return this.update(category, `api/categories/update`) } - public deleteCategory(id: string): Observable { - return this._http.delete(`api/categories/remove?id=${id}`) - .map(response => response.text()); + public deleteCategory(category: Category): Observable { + return this.deleteItem(category, `api/categories/remove?id=${category.id}`); } } diff --git a/src/BoardZ/app/services/dashboardService.ts b/src/BoardZ/app/services/dashboardService.ts index f38bbb8..58586ea 100644 --- a/src/BoardZ/app/services/dashboardService.ts +++ b/src/BoardZ/app/services/dashboardService.ts @@ -1,5 +1,4 @@ import {Injectable} from '@angular/core'; -import {Headers} from '@angular/http'; import {Observable} from 'rxjs/Observable'; import {AuthenticatedHttp} from './authenticatedHttp'; @@ -8,16 +7,6 @@ export class DashboardService { constructor(private _http: AuthenticatedHttp) { } - private getRequestOptions() { - let headers = new Headers(); - headers.append('Accept', 'application/json'); - headers.append('Accept', 'text/plain'); - headers.append('Accept', '*/*'); - headers.append('Content-Type', 'application/json;charset=UTF-8'); - - return { headers: headers }; - } - public getGameCount(): Observable { return this._http.get('api/games/count').map(response => (response.text())); } diff --git a/src/BoardZ/app/services/gamesService.ts b/src/BoardZ/app/services/gamesService.ts index 155bb9d..fa6e7a4 100644 --- a/src/BoardZ/app/services/gamesService.ts +++ b/src/BoardZ/app/services/gamesService.ts @@ -1,55 +1,39 @@ import {Injectable} from '@angular/core'; -import {Headers} from '@angular/http'; import {Observable} from 'rxjs/Observable'; import {AuthenticatedHttp} from './authenticatedHttp'; import {Game} from '../models/game'; +import {BaseApiService} from './baseApiService'; +import {OfflineStorageService} from './offlineStorageService'; import {OfflineDetectionService} from './offlineDetectionService'; @Injectable() -export class GamesService { - constructor(private _http: AuthenticatedHttp, - private _offlineDetectionService: OfflineDetectionService) { +export class GamesService extends BaseApiService { + constructor(http: AuthenticatedHttp, offlineStorageService: OfflineStorageService, offlineDetectionService: OfflineDetectionService) { + super(http, offlineStorageService, offlineDetectionService); + super.initializeEntity(Game); } - private getRequestOptions() { - let headers = new Headers(); - headers.append('Accept', 'application/json'); - headers.append('Accept', 'text/plain'); - headers.append('Accept', '*/*'); - headers.append('Content-Type', 'application/json;charset=UTF-8'); - - return { headers: headers }; - } - - public getAll(): Observable> { - - return Observable.if(()=>{ return this._offlineDetectionService.isOnline }, this._http.get('api/games/list') - .map(response => response.json()) - .map(rawGames => rawGames.map(game => Game.fromRawJson(game))),Observable.of([])); - + public getAllGames(): Observable { + return this.getAll('api/games/list'); } public deepClone(game: Game): Game { return JSON.parse(JSON.stringify(game)); } - public getById(id: string): Observable { - return this._http.get(`api/games/single?id=${id}`) - .map(response => response.json()); + public getGameById(id: string): Observable { + return this.getSingle(id, `api/games/single?id=${id}`); } public addGame(game: Game): Observable { - return this._http.post(`api/games/add`, JSON.stringify(game), this.getRequestOptions()) - .map(response => response.json()); + return this.add(game, `api/games/add`); } - public updateGame(game: Game): Observable { - return this._http.put(`api/games/update`, JSON.stringify(game), this.getRequestOptions()) - .map(response => game.id); + public updateGame(game: Game): Observable { + return this.update(game, `api/games/update`); } - public deleteGame(id: string): Observable { - return this._http.delete(`api/games/remove?id=${id}`) - .map(response => response.text()); + public deleteGame(game: Game): Observable { + return this.deleteItem(game, `api/games/remove?id=${game.id}`); } } diff --git a/src/BoardZ/app/services/logService.ts b/src/BoardZ/app/services/logService.ts index 25a557e..7fab71b 100644 --- a/src/BoardZ/app/services/logService.ts +++ b/src/BoardZ/app/services/logService.ts @@ -43,7 +43,7 @@ export class LogService { } protected doLog(formattedMessage: string): void { - // here to be overriden + } protected getIsoDate(): string { diff --git a/src/BoardZ/app/services/offlineStorageService.ts b/src/BoardZ/app/services/offlineStorageService.ts new file mode 100644 index 0000000..f618efc --- /dev/null +++ b/src/BoardZ/app/services/offlineStorageService.ts @@ -0,0 +1,37 @@ +import {Injectable} from '@angular/core'; +import {ISupportsOfflineStorage} from '../interfaces/supportsOfflineStorage'; +import {ModelState} from '../models/modelState'; + +@Injectable() +export class OfflineStorageService { + + public getAll(): Promise> { + return new Promise((resolve, reject) => { + resolve([]); + }); + } + + public getById(id: string): Promise { + return new Promise((resolve, reject) => { + resolve(null); + }); + } + + public add(item: T): Promise { + return new Promise((resolve, reject) => { + item.state = ModelState.New && resolve('123'); + }); + } + + public update(item: T): Promise { + return new Promise((resolve, reject) => { + item.state = ModelState.Modified && resolve(true); + }); + } + + public deleteItem(item: T): Promise { + return new Promise((resolve, reject) => { + item.state = ModelState.Deleted && resolve(true); + }); + } +} diff --git a/src/BoardZ/app/services/playersService.ts b/src/BoardZ/app/services/playersService.ts index 2e0d9d3..d12926e 100644 --- a/src/BoardZ/app/services/playersService.ts +++ b/src/BoardZ/app/services/playersService.ts @@ -1,5 +1,4 @@ import {Injectable} from '@angular/core'; -import {Headers} from '@angular/http'; import {Observable} from 'rxjs/Observable'; import {GeoLocation} from '../models/geoLocation'; import {AuthenticatedHttp} from './authenticatedHttp'; @@ -11,24 +10,6 @@ export class PlayersService { constructor(private _http: AuthenticatedHttp) { } - private getRequestOptions() { - let headers = new Headers(); - headers.append('Accept', 'application/json'); - headers.append('Accept', 'text/plain'); - headers.append('Accept', '*/*'); - headers.append('Content-Type', 'application/json;charset=UTF-8'); - - return { headers: headers }; - } - - public getAll(): Observable { - return this._http.get('api/players/list').map(response => (response.json())); - } - - public getById(id: string): Observable { - return this._http.get(`api/players/single?id=${id}`).map(response => response.json()); - } - public findNearby(radius: number, coordinates: GeoLocation): Observable { return this._http.get(`api/players/FindNearby?radius=${radius}&coordinate.latitude=${coordinates.latitude}&coordinate.longitude=${coordinates.longitude}`) .map(r => { @@ -37,12 +18,12 @@ export class PlayersService { } public add(player: Player): Observable { - return this._http.post(`api/players/add`, JSON.stringify(player), this.getRequestOptions()) + return this._http.post(`api/players/add`, JSON.stringify(player)) .map(response => response.json()); } public update(player: any): Observable { - return this._http.put(`api/players/update`, JSON.stringify(player), this.getRequestOptions()) + return this._http.put(`api/players/update`, JSON.stringify(player)) .map(response => player.id); } From 36d579c9c5f879c615ab8611a56581180660ca5d Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Sun, 18 Sep 2016 21:28:20 +0200 Subject: [PATCH 34/53] Reflect ModelState on the server closes #22 --- .../BoardGame.Api/BoardGame.Api.csproj | 1 + src/BoardZApi/BoardGame.Api/BoardzContext.cs | 12 +++-- .../BoardGame.Api/Models/Category.cs | 27 +++++++++-- src/BoardZApi/BoardGame.Api/Models/Game.cs | 28 +++++++++-- .../BoardGame.Api/Models/ModelState.cs | 32 +++++++++++++ src/BoardZApi/docs/BoardGame.Api.xml | 47 ++++++++++++++++++- 6 files changed, 132 insertions(+), 15 deletions(-) create mode 100644 src/BoardZApi/BoardGame.Api/Models/ModelState.cs diff --git a/src/BoardZApi/BoardGame.Api/BoardGame.Api.csproj b/src/BoardZApi/BoardGame.Api/BoardGame.Api.csproj index 0b04ba4..94c94fe 100644 --- a/src/BoardZApi/BoardGame.Api/BoardGame.Api.csproj +++ b/src/BoardZApi/BoardGame.Api/BoardGame.Api.csproj @@ -152,6 +152,7 @@ + diff --git a/src/BoardZApi/BoardGame.Api/BoardzContext.cs b/src/BoardZApi/BoardGame.Api/BoardzContext.cs index 0dbca19..a89fca2 100644 --- a/src/BoardZApi/BoardGame.Api/BoardzContext.cs +++ b/src/BoardZApi/BoardGame.Api/BoardzContext.cs @@ -1,4 +1,4 @@ -using BoardGame.Api.Models; +using BoardGame.Api.Models; using System.ComponentModel.DataAnnotations.Schema; using System.Data.Entity; using System.Data.Entity.Infrastructure.Annotations; @@ -103,10 +103,14 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) modelBuilder.Entity().Property(game => game.RowVersion).IsRowVersion(); - modelBuilder.Entity().Ignore(game => game.RowVersionAsInt); + modelBuilder.Entity() + .Ignore(game => game.ModelState) + .Ignore(game=>game.RowVersionAsInt); + modelBuilder.Entity().Property(category => category.RowVersion).IsRowVersion(); - modelBuilder.Entity().Ignore(category => category.RowVersionAsInt); - + modelBuilder.Entity() + .Ignore(category => category.ModelState) + .Ignore(category => category.RowVersionAsInt); } } } diff --git a/src/BoardZApi/BoardGame.Api/Models/Category.cs b/src/BoardZApi/BoardGame.Api/Models/Category.cs index 45f8ed7..49dbef5 100644 --- a/src/BoardZApi/BoardGame.Api/Models/Category.cs +++ b/src/BoardZApi/BoardGame.Api/Models/Category.cs @@ -1,4 +1,4 @@ -using BoardGame.Api.Models; +using BoardGame.Api.Models; using Newtonsoft.Json; using System; using System.Collections.Generic; @@ -51,23 +51,40 @@ public IList GameNames { get { - return this.Games.Where(game => game.UserName.Equals(this.UserName,StringComparison.InvariantCultureIgnoreCase)).Select(game => game.Name).ToList(); + return this.Games.Where(game => game.UserName.Equals(this.UserName, StringComparison.InvariantCultureIgnoreCase)).Select(game => game.Name).ToList(); } } /// /// NumberOfGames /// - public int NumberOfGames => this.GameNames.Count; + public int NumberOfGames => this.GameNames.Count; /// - /// RowVersion -> required for Offline Support + /// Category Row Version /// + [JsonIgnore] public byte[] RowVersion { get; set; } /// /// the version that goes to the client /// - public ulong RowVersionAsInt => BitConverter.ToUInt64(RowVersion.Reverse().ToArray(), 0); + public ulong RowVersionAsInt + { + get + { + if (RowVersion != null) + { + return BitConverter.ToUInt64(RowVersion.Reverse().ToArray(), 0); + } + return 0; + } + } + + /// + /// ModelState -> will be provided by the client when syncinc after connection was lost + /// + [JsonIgnore] + public ModelState ModelState { get; set; } } } diff --git a/src/BoardZApi/BoardGame.Api/Models/Game.cs b/src/BoardZApi/BoardGame.Api/Models/Game.cs index b18e1d0..38ced99 100644 --- a/src/BoardZApi/BoardGame.Api/Models/Game.cs +++ b/src/BoardZApi/BoardGame.Api/Models/Game.cs @@ -1,4 +1,4 @@ -using System; +using System; using Newtonsoft.Json; using System.Collections.Generic; using System.Linq; @@ -15,7 +15,7 @@ public class Game /// public Game() { - + } /// /// Unique identifier @@ -25,7 +25,7 @@ public Game() /// /// Name of the board game /// - public string Name { get; set; } + public string Name { get; set; } /// /// Edition of the Game @@ -60,11 +60,29 @@ public Game() /// /// RowVersion -> required for Offline Support /// + [JsonIgnore] public byte[] RowVersion { get; set; } /// /// the version that goes to the client /// - public ulong RowVersionAsInt => BitConverter.ToUInt64(RowVersion.Reverse().ToArray(), 0); + public ulong RowVersionAsInt + { + get + { + if (RowVersion != null) + { + return BitConverter.ToUInt64(RowVersion.Reverse().ToArray(), 0); + } + return 0; + } + } + + /// + /// ModelState -> will be provided by the client when syncinc after connection was lost + /// + [JsonIgnore] + public ModelState ModelState { get; set; } + } -} \ No newline at end of file +} diff --git a/src/BoardZApi/BoardGame.Api/Models/ModelState.cs b/src/BoardZApi/BoardGame.Api/Models/ModelState.cs new file mode 100644 index 0000000..0204f90 --- /dev/null +++ b/src/BoardZApi/BoardGame.Api/Models/ModelState.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BoardGame.Api.Models +{ + /// + /// Model State enum + /// + public enum ModelState + { + /// + /// No Changes + /// + Clean = 0, + /// + /// New Entity + /// + New = 1, + /// + /// Modified Entity + /// + Modified = 2, + /// + /// Entity has been deleted on the client + /// + Deleted = 3 + } + +} diff --git a/src/BoardZApi/docs/BoardGame.Api.xml b/src/BoardZApi/docs/BoardGame.Api.xml index 0c34965..8267930 100644 --- a/src/BoardZApi/docs/BoardGame.Api.xml +++ b/src/BoardZApi/docs/BoardGame.Api.xml @@ -396,6 +396,16 @@ RowVersion -> required for Offline Support + + + the version that goes to the client + + + + + ModelState -> will be provided by the client when syncinc after connection was lost + + Category model @@ -438,7 +448,17 @@ - RowVersion -> required for Offline Support + Category Row Version + + + + + the version that goes to the client + + + + + ModelState -> will be provided by the client when syncinc after connection was lost @@ -461,6 +481,31 @@ Longitude value + + + Model State enum + + + + + No Changes + + + + + New Entity + + + + + Modified Entity + + + + + Entity has been deleted on the client + + Unique identifier From 22b062133aaf440b2d2f8a3cde6eaad012cb144f Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Sun, 18 Sep 2016 22:02:06 +0200 Subject: [PATCH 35/53] Implemented SyncController and GetChanges Since / aligned Service naming fixes #16 fixes #8 --- .../BoardGame.Api/BoardGame.Api.csproj | 6 +- src/BoardZApi/BoardGame.Api/BoardzContext.cs | 4 +- .../Controllers/AgeRatingsController.cs | 4 +- .../Controllers/BaseApiController.cs | 8 +- .../Controllers/CategoriesController.cs | 15 +++ .../Controllers/GamesController.cs | 6 +- .../Controllers/PlayersController.cs | 4 +- .../Controllers/SyncController.cs | 92 ++++++++++++++++++- ...eRatingService.cs => AgeRatingsService.cs} | 6 +- .../Services/CategoriesService.cs | 25 ++++- .../{GameService.cs => GamesService.cs} | 12 ++- .../{PlayerService.cs => PlayersService.cs} | 4 +- src/BoardZApi/docs/BoardGame.Api.xml | 58 ++++++++---- 13 files changed, 188 insertions(+), 56 deletions(-) rename src/BoardZApi/BoardGame.Api/Services/{AgeRatingService.cs => AgeRatingsService.cs} (80%) rename src/BoardZApi/BoardGame.Api/Services/{GameService.cs => GamesService.cs} (91%) rename src/BoardZApi/BoardGame.Api/Services/{PlayerService.cs => PlayersService.cs} (98%) diff --git a/src/BoardZApi/BoardGame.Api/BoardGame.Api.csproj b/src/BoardZApi/BoardGame.Api/BoardGame.Api.csproj index 94c94fe..3a8e59d 100644 --- a/src/BoardZApi/BoardGame.Api/BoardGame.Api.csproj +++ b/src/BoardZApi/BoardGame.Api/BoardGame.Api.csproj @@ -159,10 +159,10 @@ - + - - + + diff --git a/src/BoardZApi/BoardGame.Api/BoardzContext.cs b/src/BoardZApi/BoardGame.Api/BoardzContext.cs index a89fca2..227b610 100644 --- a/src/BoardZApi/BoardGame.Api/BoardzContext.cs +++ b/src/BoardZApi/BoardGame.Api/BoardzContext.cs @@ -105,8 +105,8 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) modelBuilder.Entity().Property(game => game.RowVersion).IsRowVersion(); modelBuilder.Entity() .Ignore(game => game.ModelState) - .Ignore(game=>game.RowVersionAsInt); - + .Ignore(game => game.RowVersionAsInt); + modelBuilder.Entity().Property(category => category.RowVersion).IsRowVersion(); modelBuilder.Entity() .Ignore(category => category.ModelState) diff --git a/src/BoardZApi/BoardGame.Api/Controllers/AgeRatingsController.cs b/src/BoardZApi/BoardGame.Api/Controllers/AgeRatingsController.cs index 18c6925..8015e37 100644 --- a/src/BoardZApi/BoardGame.Api/Controllers/AgeRatingsController.cs +++ b/src/BoardZApi/BoardGame.Api/Controllers/AgeRatingsController.cs @@ -17,13 +17,13 @@ namespace BoardGame.Api.Controllers [Authorize] public class AgeRatingsController : ApiController, IDisposable { - private readonly AgeRatingService _ageRatingService; + private readonly AgeRatingsService _ageRatingService; /// /// Default CTOR /// public AgeRatingsController() { - _ageRatingService = new AgeRatingService(); + _ageRatingService = new AgeRatingsService(); } /// diff --git a/src/BoardZApi/BoardGame.Api/Controllers/BaseApiController.cs b/src/BoardZApi/BoardGame.Api/Controllers/BaseApiController.cs index b2f5732..d75d1c4 100644 --- a/src/BoardZApi/BoardGame.Api/Controllers/BaseApiController.cs +++ b/src/BoardZApi/BoardGame.Api/Controllers/BaseApiController.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Web.Http; namespace BoardGame.Api.Controllers @@ -17,9 +13,9 @@ public class BaseApiController: ApiController /// /// /// - protected byte[] GetRowVersion(string rowVersion = null) + protected byte[] GetRowVersion(int? rowVersion) { - return rowVersion != null ? Convert.FromBase64String(rowVersion) : null; + return rowVersion.HasValue ? Convert.FromBase64String(rowVersion.Value.ToString()) : null; } } } diff --git a/src/BoardZApi/BoardGame.Api/Controllers/CategoriesController.cs b/src/BoardZApi/BoardGame.Api/Controllers/CategoriesController.cs index 918a965..d5210e6 100644 --- a/src/BoardZApi/BoardGame.Api/Controllers/CategoriesController.cs +++ b/src/BoardZApi/BoardGame.Api/Controllers/CategoriesController.cs @@ -25,6 +25,21 @@ public CategoriesController() _categoriesService = new CategoriesService(); } + /// + /// Method for loading categories since a given row version + /// + /// + /// + [HttpGet] + public IEnumerable Since(int? rowVersion) + { + var rv = this.GetRowVersion(rowVersion); + var username = User.GetCurrentUsernameOrThrow(); + var games = _categoriesService.GetAll(username, rv); + + return games; + } + /// /// Lists all categories /// diff --git a/src/BoardZApi/BoardGame.Api/Controllers/GamesController.cs b/src/BoardZApi/BoardGame.Api/Controllers/GamesController.cs index a5c509a..06c8bb0 100644 --- a/src/BoardZApi/BoardGame.Api/Controllers/GamesController.cs +++ b/src/BoardZApi/BoardGame.Api/Controllers/GamesController.cs @@ -14,14 +14,14 @@ namespace BoardGame.Api.Controllers [Authorize] public class GamesController : BaseApiController { - private readonly GameService _gameService; + private readonly GamesService _gameService; /// /// default CTOR /// public GamesController() { - _gameService = new GameService(); + _gameService = new GamesService(); } /// @@ -30,7 +30,7 @@ public GamesController() /// /// [HttpGet] - public IEnumerable Since(string rowVersion = null) + public IEnumerable Since(int? rowVersion) { var rv = this.GetRowVersion(rowVersion); var username = User.GetCurrentUsernameOrThrow(); diff --git a/src/BoardZApi/BoardGame.Api/Controllers/PlayersController.cs b/src/BoardZApi/BoardGame.Api/Controllers/PlayersController.cs index 8aacd90..872c861 100644 --- a/src/BoardZApi/BoardGame.Api/Controllers/PlayersController.cs +++ b/src/BoardZApi/BoardGame.Api/Controllers/PlayersController.cs @@ -12,14 +12,14 @@ namespace BoardGame.Api.Controllers [Authorize] public class PlayersController : ApiController, IDisposable { - private readonly PlayerService _playersService; + private readonly PlayersService _playersService; /// /// default ctor /// public PlayersController() { - _playersService = new PlayerService(); + _playersService = new PlayersService(); } /// diff --git a/src/BoardZApi/BoardGame.Api/Controllers/SyncController.cs b/src/BoardZApi/BoardGame.Api/Controllers/SyncController.cs index 8cd7a7e..1390ad7 100644 --- a/src/BoardZApi/BoardGame.Api/Controllers/SyncController.cs +++ b/src/BoardZApi/BoardGame.Api/Controllers/SyncController.cs @@ -1,4 +1,6 @@ -using BoardGame.Api.Models; +using BoardGame.Api.Helpers; +using BoardGame.Api.Models; +using BoardGame.Api.Services; using System; using System.Collections.Generic; using System.Linq; @@ -10,8 +12,24 @@ namespace BoardGame.Api.Controllers { + /// + /// SnycController + /// + [Authorize] public class SyncController : ApiController { + private GamesService _gamesService; + private CategoriesService _categoriesService; + + /// + /// Sync Controller Default CTOR + /// + public SyncController() + { + _gamesService = new GamesService(); + _categoriesService = new CategoriesService(); + } + /// /// Sync games /// @@ -20,8 +38,41 @@ public class SyncController : ApiController [HttpPost] public HttpResponseMessage SyncGames([FromBody]IEnumerable games) { - // will return latest rowVersion after sync - return Request.CreateResponse(HttpStatusCode.OK, 123123123); + if (games == null) + { + return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, new HttpError("No data passed")); + } + var userName = User.GetCurrentUsernameOrThrow(); + var transaction = _gamesService.NewTransaction(); + try + { + games + .Where(game => game.ModelState == Models.ModelState.New) + .ToList() + .ForEach(game => _gamesService.AddGame(game, userName)); + + games + .Where(game => game.ModelState == Models.ModelState.Modified) + .ToList() + .ForEach(game => _gamesService.Update(game, userName)); + + games + .Where(game => game.ModelState == Models.ModelState.Deleted) + .Select(game => game.Id) + .ToList() + .ForEach(gameId => _gamesService.DeleteGame(gameId)); + } + catch(Exception ex) + { + transaction.Rollback(); + return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex); + } + finally + { + transaction.Commit(); + } + return Request.CreateResponse(HttpStatusCode.OK); + } /// @@ -33,7 +84,40 @@ public HttpResponseMessage SyncGames([FromBody]IEnumerable games) public HttpResponseMessage SyncCategories([FromBody]IEnumerable categories) { // will return latest rowVersion after sync - return Request.CreateResponse(HttpStatusCode.OK, 123123123); + if (categories == null) + { + return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, new HttpError("No data passed")); + } + var userName = User.GetCurrentUsernameOrThrow(); + var transaction = _categoriesService.NewTransaction(); + try + { + categories + .Where(category => category.ModelState == Models.ModelState.New) + .ToList() + .ForEach(category => _categoriesService.AddCategory(category, userName)); + + categories + .Where(category => category.ModelState == Models.ModelState.Modified) + .ToList() + .ForEach(category => _categoriesService.Update(category, userName)); + + categories + .Where(category => category.ModelState == Models.ModelState.Deleted) + .Select(category => category.Id) + .ToList() + .ForEach(categoryId => _categoriesService.DeleteCategory(categoryId)); + } + catch (Exception ex) + { + transaction.Rollback(); + return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex); + } + finally + { + transaction.Commit(); + } + return Request.CreateResponse(HttpStatusCode.OK); } diff --git a/src/BoardZApi/BoardGame.Api/Services/AgeRatingService.cs b/src/BoardZApi/BoardGame.Api/Services/AgeRatingsService.cs similarity index 80% rename from src/BoardZApi/BoardGame.Api/Services/AgeRatingService.cs rename to src/BoardZApi/BoardGame.Api/Services/AgeRatingsService.cs index 86705e2..652f8f3 100644 --- a/src/BoardZApi/BoardGame.Api/Services/AgeRatingService.cs +++ b/src/BoardZApi/BoardGame.Api/Services/AgeRatingsService.cs @@ -1,16 +1,14 @@ using BoardGame.Api.Models; using System; -using System.Linq; using System.Collections.Generic; -using System.Data.Entity; namespace BoardGame.Api.Services { - internal class AgeRatingService : IDisposable + internal class AgeRatingsService : IDisposable { private readonly BoardzContext _dbContext; - public AgeRatingService() + public AgeRatingsService() { _dbContext = new BoardzContext(); } diff --git a/src/BoardZApi/BoardGame.Api/Services/CategoriesService.cs b/src/BoardZApi/BoardGame.Api/Services/CategoriesService.cs index 2b226f4..2240041 100644 --- a/src/BoardZApi/BoardGame.Api/Services/CategoriesService.cs +++ b/src/BoardZApi/BoardGame.Api/Services/CategoriesService.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Data.Entity; +using BoardGame.Api.EntityFrameworkExtensions; namespace BoardGame.Api.Services { @@ -22,7 +23,7 @@ public CategoriesService() /// public void Dispose() { - if(_dbContext != null) + if (_dbContext != null) { _dbContext.Dispose(); } @@ -32,10 +33,19 @@ public void Dispose() /// Return all Categories from a given user /// /// + /// /// - public IEnumerable GetAll(String userName) + public IEnumerable GetAll(String userName, byte[] rowVersion = null) { - return _dbContext.Categories.Include(category=>category.Games) + if (rowVersion != null) + { + return _dbContext.Categories + .Include(category => category.Games) + .Where(category => category.UserName.Equals(userName, StringComparison.InvariantCultureIgnoreCase)).OrderBy(category => category.Name) + .Where(category => category.RowVersion.Compare(rowVersion) > 0).ToList(); + } + return _dbContext.Categories + .Include(category => category.Games) .Where(category => category.UserName.Equals(userName, StringComparison.InvariantCultureIgnoreCase)).OrderBy(category => category.Name); } @@ -48,7 +58,7 @@ public IEnumerable GetAll(String userName) public Category GetById(Guid categoryId, String userName) { return _dbContext.Categories - .Include(category => category.Games.Where(game=>game.UserName.Equals(userName,StringComparison.InvariantCultureIgnoreCase))) + .Include(category => category.Games.Where(game => game.UserName.Equals(userName, StringComparison.InvariantCultureIgnoreCase))) .Where(category => category.UserName.Equals(userName, StringComparison.InvariantCultureIgnoreCase)) .FirstOrDefault(category => category.Id.Equals(categoryId)); } @@ -85,7 +95,7 @@ public bool Update(Category category, String userName) } } - + /// /// Delete a category by it's id /// @@ -115,6 +125,11 @@ public bool DeleteCategory(Guid categoryId) } } + internal DbContextTransaction NewTransaction() + { + return _dbContext.Database.BeginTransaction(); + } + /// /// Create a new Category /// diff --git a/src/BoardZApi/BoardGame.Api/Services/GameService.cs b/src/BoardZApi/BoardGame.Api/Services/GamesService.cs similarity index 91% rename from src/BoardZApi/BoardGame.Api/Services/GameService.cs rename to src/BoardZApi/BoardGame.Api/Services/GamesService.cs index 486c4a0..4675dcd 100644 --- a/src/BoardZApi/BoardGame.Api/Services/GameService.cs +++ b/src/BoardZApi/BoardGame.Api/Services/GamesService.cs @@ -10,14 +10,14 @@ namespace BoardGame.Api.Services /// /// Game Service /// - public class GameService : IDisposable + public class GamesService : IDisposable { private readonly BoardzContext _dbContext; /// /// Default CTOR /// - public GameService() + public GamesService() { _dbContext = new BoardzContext(); } @@ -35,7 +35,8 @@ public IEnumerable GetAll(string userName, byte[] rowVersion = null) return _dbContext.Games .Include(game => game.AgeRating) .Include(game => game.Categories) - .Where(c => c.RowVersion.Compare(rowVersion) > 0).ToList(); + .Where(game => game.UserName.Equals(userName, StringComparison.InvariantCultureIgnoreCase)) + .Where(game => game.RowVersion.Compare(rowVersion) > 0).ToList(); } return _dbContext.Games .Include(game => game.AgeRating) @@ -137,6 +138,11 @@ public Guid AddGame(Game game, String userName) return game.Id; } + internal DbContextTransaction NewTransaction() + { + return _dbContext.Database.BeginTransaction(); + } + /// /// IDisposable /// diff --git a/src/BoardZApi/BoardGame.Api/Services/PlayerService.cs b/src/BoardZApi/BoardGame.Api/Services/PlayersService.cs similarity index 98% rename from src/BoardZApi/BoardGame.Api/Services/PlayerService.cs rename to src/BoardZApi/BoardGame.Api/Services/PlayersService.cs index 724ca4f..d6d05f9 100644 --- a/src/BoardZApi/BoardGame.Api/Services/PlayerService.cs +++ b/src/BoardZApi/BoardGame.Api/Services/PlayersService.cs @@ -11,14 +11,14 @@ namespace BoardGame.Api.Services /// /// PlayerService /// - public class PlayerService : IDisposable + public class PlayersService : IDisposable { private readonly BoardzContext _dbContext; private readonly DistanceCalculator _distanceCalculator; /// /// Default CTOR /// - public PlayerService() + public PlayersService() { _dbContext = new BoardzContext(); _distanceCalculator = new DistanceCalculator(); diff --git a/src/BoardZApi/docs/BoardGame.Api.xml b/src/BoardZApi/docs/BoardGame.Api.xml index 8267930..d5aa714 100644 --- a/src/BoardZApi/docs/BoardGame.Api.xml +++ b/src/BoardZApi/docs/BoardGame.Api.xml @@ -88,7 +88,7 @@ Base ApiController class - + Converts a string or null to a valid RowVersion @@ -105,6 +105,13 @@ default CTOR + + + Method for loading categories since a given row version + + + + Lists all categories @@ -161,7 +168,7 @@ default CTOR - + Method for loading games since a given row version @@ -289,6 +296,16 @@ Ping endpoint - Just returning a HTTP StatusCode 200 + + + SnycController + + + + + Sync Controller Default CTOR + + Sync games @@ -576,11 +593,12 @@ IDisposable - + Return all Categories from a given user + @@ -621,17 +639,17 @@ - + Game Service - + Default CTOR - + Returns all games for a given user @@ -639,7 +657,7 @@ - + Get a game by it's ID @@ -647,14 +665,14 @@ - + Get Games Count - + Update an existing game @@ -662,14 +680,14 @@ - + Delete a game by it's Id - + Add a game @@ -677,22 +695,22 @@ - + IDisposable - + PlayerService - + Default CTOR - + Return a list of playeres (playing since 5 days :D) playing nearby a given coordiante @@ -700,34 +718,34 @@ - + Get the number of active players - + Checkin - + Delete a player by his/her id - + update an existing player instance - + IDisposable From 46a3520c824511f124a1d960fa6d9967f11bf464 Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Tue, 20 Sep 2016 12:35:05 +0200 Subject: [PATCH 36/53] take it offline using DexieJS --- gulp.config.js | 2 + {gulpTasks => gulp}/cordova.js | 0 {gulpTasks => gulp}/electron.js | 0 {gulpTasks => gulp}/web.js | 5 ++ gulpfile.js | 4 +- package.json | 1 + .../app/components/categories/details.ts | 2 +- src/BoardZ/app/models/ageRating.ts | 2 +- src/BoardZ/app/models/game.ts | 3 +- src/BoardZ/app/modules/config.ts | 2 + src/BoardZ/app/services/ageRatingsService.ts | 8 +-- src/BoardZ/app/services/baseApiService.ts | 42 ++++++------- src/BoardZ/app/services/categoriesService.ts | 59 +++++++++++++++---- src/BoardZ/app/services/dashboardService.ts | 25 ++++++-- src/BoardZ/app/services/databaseService.ts | 23 ++++++++ src/BoardZ/app/services/gamesService.ts | 21 ++++--- .../app/services/offlineStorageService.ts | 6 ++ src/BoardZ/system.setup.js | 8 +-- 18 files changed, 153 insertions(+), 60 deletions(-) rename {gulpTasks => gulp}/cordova.js (100%) rename {gulpTasks => gulp}/electron.js (100%) rename {gulpTasks => gulp}/web.js (96%) create mode 100644 src/BoardZ/app/services/databaseService.ts diff --git a/gulp.config.js b/gulp.config.js index 1683264..c2c5d57 100644 --- a/gulp.config.js +++ b/gulp.config.js @@ -75,9 +75,11 @@ module.exports = { './node_modules/core-js/client/shim.min.js', './node_modules/zone.js/dist/zone.js', './node_modules/reflect-metadata/reflect.js' + ], angular2: './node_modules/@angular/**/*', rxjs: './node_modules/rxjs/**/*', + dexie: './node_modules/dexie/dist/dexie.js', systemJs: './node_modules/systemjs/dist/system.src.js' } }, diff --git a/gulpTasks/cordova.js b/gulp/cordova.js similarity index 100% rename from gulpTasks/cordova.js rename to gulp/cordova.js diff --git a/gulpTasks/electron.js b/gulp/electron.js similarity index 100% rename from gulpTasks/electron.js rename to gulp/electron.js diff --git a/gulpTasks/web.js b/gulp/web.js similarity index 96% rename from gulpTasks/web.js rename to gulp/web.js index cd64c08..eab9f2d 100644 --- a/gulpTasks/web.js +++ b/gulp/web.js @@ -65,6 +65,10 @@ return gulp.src(config.source.files.rxjs) .pipe(gulp.dest(path.join(config.targets.buildFolder, 'rxjs'))); }); + gulp.task('[private-web]:copy-dexies-scripts', function () { + return gulp.src(config.source.files.dexie) + .pipe(gulp.dest(path.join(config.targets.buildFolder, 'dexie'))); + }); gulp.task('[private-web]:copy-system-setup-script', function () { return gulp.src(config.source.files.systemSetupScript) @@ -137,6 +141,7 @@ '[private-web]:bundle-vendor-scripts', '[private-web]:copy-angular2-scripts', '[private-web]:copy-rxjs-scripts', + '[private-web]:copy-dexies-scripts', '[private-web]:copy-system-setup-script', '[private-web]:copy-cordova-script', '[private-web]:copy-system', diff --git a/gulpfile.js b/gulpfile.js index 7b0a31a..104d003 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -7,7 +7,7 @@ gulp = require('gulp'), del = require('del'), runSequence = require('run-sequence'), - gulptasks = require('require-dir')('./gulpTasks'); + gulptasks = require('require-dir')('./gulp'); for (var gulpTask in gulptasks) { @@ -35,4 +35,4 @@ ); }); -})(); \ No newline at end of file +})(); diff --git a/package.json b/package.json index 9b8891b..6b5d872 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "@angular/platform-browser-dynamic": "2.0.0", "@angular/router": "^3.0.0", "core-js": "^2.4.1", + "dexie": "^1.4.2", "reflect-metadata": "^0.1.3", "rxjs": "5.0.0-beta.12", "systemjs": "0.19.27", diff --git a/src/BoardZ/app/components/categories/details.ts b/src/BoardZ/app/components/categories/details.ts index d38c787..4c8b46c 100644 --- a/src/BoardZ/app/components/categories/details.ts +++ b/src/BoardZ/app/components/categories/details.ts @@ -75,7 +75,7 @@ export class CategoryDetailsComponent implements OnInit { public deleteCategory(): void { if (window.confirm('Really delete the category "' + this.originalModel.name + '" ?')) { - this._categoriesService.deleteCategory(this.originalModel.id) + this._categoriesService.deleteCategory(this.originalModel) .subscribe( () => { this._notificationService.notifySuccess('Category data was deleted.'); diff --git a/src/BoardZ/app/models/ageRating.ts b/src/BoardZ/app/models/ageRating.ts index 5009f74..91ac710 100644 --- a/src/BoardZ/app/models/ageRating.ts +++ b/src/BoardZ/app/models/ageRating.ts @@ -1,7 +1,7 @@ import {ISupportsOfflineStorage} from '../interfaces/supportsOfflineStorage'; import {ModelState} from './modelState'; -export class AgeRating implements ISupportsOfflineStorage { +export class AgeRating implements ISupportsOfflineStorage { constructor() { this.state = ModelState.Clean; diff --git a/src/BoardZ/app/models/game.ts b/src/BoardZ/app/models/game.ts index 8a4f055..63c809c 100644 --- a/src/BoardZ/app/models/game.ts +++ b/src/BoardZ/app/models/game.ts @@ -2,7 +2,7 @@ import {ModelState} from './modelState'; import {AgeRating} from './ageRating'; import {Category} from './category'; import {ISupportsOfflineStorage} from '../interfaces/supportsOfflineStorage'; -export class Game implements ISupportsOfflineStorage { +export class Game implements ISupportsOfflineStorage { constructor() { this.state = ModelState.Clean; @@ -27,6 +27,7 @@ export class Game implements ISupportsOfflineStorage { this.id = rawJson.id || null; this.name = rawJson.name || null; + //noinspection TypeScriptUnresolvedFunction this.ageRating = (new AgeRating()).fromRawJson(rawJson.ageRating); this.ageRatingId = this.ageRating.id || rawJson.ageRatingId; if(rawJson.categories){ diff --git a/src/BoardZ/app/modules/config.ts b/src/BoardZ/app/modules/config.ts index 0580339..20748f9 100644 --- a/src/BoardZ/app/modules/config.ts +++ b/src/BoardZ/app/modules/config.ts @@ -47,6 +47,7 @@ import {CategoryDetailsResolver} from '../resolvers/categoryDetailsResolver'; import {OfflineStorageService} from '../services/offlineStorageService'; import {Game} from '../models/game'; import {Category} from '../models/category'; +import {DatabaseService} from '../services/databaseService'; export namespace ModuleConfiguration { @@ -80,6 +81,7 @@ export namespace ModuleConfiguration { OfflineConfig, OfflineDetectionService, OfflineStorageService, + DatabaseService, NativeIntegrationService, AuthenticatedHttp, TokenService, diff --git a/src/BoardZ/app/services/ageRatingsService.ts b/src/BoardZ/app/services/ageRatingsService.ts index a3b7b6b..c6e8cee 100644 --- a/src/BoardZ/app/services/ageRatingsService.ts +++ b/src/BoardZ/app/services/ageRatingsService.ts @@ -1,7 +1,6 @@ import {Injectable} from '@angular/core'; import {AuthenticatedHttp} from './authenticatedHttp'; import {AgeRating} from '../models/ageRating'; -import {OfflineStorageService} from './offlineStorageService'; import {OfflineDetectionService} from './offlineDetectionService'; import {BaseApiService} from './baseApiService'; import {Observable} from 'rxjs/Rx'; @@ -9,18 +8,19 @@ import {Observable} from 'rxjs/Rx'; @Injectable() export class AgeRatingsService extends BaseApiService { - constructor(http: AuthenticatedHttp, offlineStorageService: OfflineStorageService, offlineDetectionService: OfflineDetectionService) { - super(http, offlineStorageService, offlineDetectionService); + constructor(http: AuthenticatedHttp, offlineDetectionService: OfflineDetectionService) { + super(http, offlineDetectionService); super.initializeEntity(AgeRating); } public initialize(): void { this.getAllAgeRatings() .subscribe(() => { + //todo: store those }); } public getAllAgeRatings(): Observable> { - return this.getAll('api/ageratings/list'); + return this.getAll('api/ageratings/list', null); } } diff --git a/src/BoardZ/app/services/baseApiService.ts b/src/BoardZ/app/services/baseApiService.ts index 0ac981c..7a0aef4 100644 --- a/src/BoardZ/app/services/baseApiService.ts +++ b/src/BoardZ/app/services/baseApiService.ts @@ -2,52 +2,50 @@ import {Injectable, Type} from '@angular/core'; import {AuthenticatedHttp} from './authenticatedHttp'; import {Observable} from 'rxjs/Rx'; import {OfflineDetectionService} from './offlineDetectionService'; -import {OfflineStorageService} from './offlineStorageService'; import {ISupportsOfflineStorage} from '../interfaces/supportsOfflineStorage'; +import {DatabaseService} from './databaseService'; @Injectable() -export class BaseApiService { +export abstract class BaseApiService { private _entityType: Type; + constructor(private _authenticatedHttp: AuthenticatedHttp, - private _offlineStorageService: OfflineStorageService, + private _databaseService: DatabaseService, private _offlineDetectionService: OfflineDetectionService) { - - } - public initializeEntity(type: Type){ + public initializeEntity(type: Type) { this._entityType = type; } /** * return all items of the generic type */ - protected getAll(url: string): Observable> { + protected getAll(url: string, offlineFallback: Observable>): Observable> { let httpObservable: Observable> = this._authenticatedHttp.get(url) .map(response => response.json()) - .map(rawJsonResults => rawJsonResults.map(rawJsonResult => (new this._entityType()).fromRawJson(rawJsonResult))); + .map(rawJsonResults => rawJsonResults.map(rawJsonResult => (new this._entityType()).fromRawJson(rawJsonResult))) + .do((results) => this._databaseService.categories.bulkAdd(results).then(()=>results)); - let offlineObservable: Observable> = Observable.fromPromise(this._offlineStorageService.getAll()); return Observable.if(()=> { return this._offlineDetectionService.isOnline; - }, httpObservable, offlineObservable); + }, httpObservable, offlineFallback); } /** * return either an item by it's identifier or null * @param id */ - protected getSingle(id: string, url: string): Observable { + protected getSingle(id: string, url: string, offlineFallback: Observable): Observable { let httpObservable = this._authenticatedHttp.get(url) .map(response => response.json()) .map(rawJsonResult => (new this._entityType()).fromRawJson(rawJsonResult)); - let offlineObservable = Observable.fromPromise(>this._offlineStorageService.getById(id)); return >Observable.if(()=> { return this._offlineDetectionService.isOnline; - }, httpObservable, offlineObservable); + }, httpObservable, offlineFallback); } @@ -55,30 +53,26 @@ export class BaseApiService { * persist the new item * @param item */ - public add(item: T, url: string): Observable { + public add(item: T, url: string, offlineFallback: Observable): Observable { let httpObservable = this._authenticatedHttp.post(url, JSON.stringify(item)) .map(response => response.text()); - let offlineObservable = Observable.fromPromise(this._offlineStorageService.add(item)); - return Observable.if(()=> { return this._offlineDetectionService.isOnline; - }, httpObservable, offlineObservable); + }, httpObservable, offlineFallback); } /** * Send an update for the item * @param item */ - public update(item: T, url: string): Observable { + public update(item: T, url: string, offlineFallback: Observable): Observable { let httpObservable = this._authenticatedHttp.put(url, JSON.stringify(item)) .map(response => response.ok); - let offlineObservable = Observable.fromPromise(this._offlineStorageService.update(item)); - return Observable.if(()=> { return this._offlineDetectionService.isOnline; - }, httpObservable, offlineObservable); + }, httpObservable, offlineFallback); } /** @@ -86,15 +80,13 @@ export class BaseApiService { * @param item * @returns {any} */ - public deleteItem(item: T, url: string): Observable { + public deleteItem(item: T, url: string, offlineFallback: Observable): Observable { let httpObservable = this._authenticatedHttp.delete(url) .map(response => response.status); - let offlineObservable = Observable.fromPromise(this._offlineStorageService.deleteItem(item)); return Observable.if(()=> { return this._offlineDetectionService.isOnline; - }, httpObservable, offlineObservable); - return Observable.of(false); + }, httpObservable, offlineFallback); } } diff --git a/src/BoardZ/app/services/categoriesService.ts b/src/BoardZ/app/services/categoriesService.ts index d50e0f7..e22f43d 100644 --- a/src/BoardZ/app/services/categoriesService.ts +++ b/src/BoardZ/app/services/categoriesService.ts @@ -3,37 +3,76 @@ import {Observable} from 'rxjs/Observable'; import {AuthenticatedHttp} from './authenticatedHttp'; import {Category} from '../models/category'; import {BaseApiService} from './baseApiService'; -import {OfflineStorageService} from './offlineStorageService'; import {OfflineDetectionService} from './offlineDetectionService'; +import {DatabaseService} from './databaseService'; +import {ModelState} from '../models/modelState'; @Injectable() export class CategoriesService extends BaseApiService { - constructor(http: AuthenticatedHttp, offlineStorageService: OfflineStorageService, offlineDetectionService: OfflineDetectionService) { - super(http, offlineStorageService, offlineDetectionService); + + constructor(private _databaseService: DatabaseService, + http: AuthenticatedHttp, + offlineDetectionService: OfflineDetectionService) { + super(http, _databaseService, offlineDetectionService); super.initializeEntity(Category); - } - public getAllCategories(): Observable> { - return this.getAll('api/categories/list'); } public deepClone(category: Category): Category { return JSON.parse(JSON.stringify(category)); } + public getAllCategories(): Observable> { + let offlineFallback = Observable.fromPromise(this._databaseService.categories.filter(c=>c.state !== ModelState.Deleted).toArray()); + return this.getAll('api/categories/list', offlineFallback); + } + public getCategoryById(id: string): Observable { - return this.getSingle(id, `api/categories/single?id=${id}`); + + return this.getSingle(id, `api/categories/single?id=${id}`, Observable.fromPromise(this._databaseService.categories.get(id))); } public addCategory(category: Category): Observable { - return this.add(category, `api/categories/add`); + return this.add(category, `api/categories/add`, this.getAddOfflineFallback(category)); } public updateCategory(category: Category): Observable { - return this.update(category, `api/categories/update`) + return this.update(category, `api/categories/update`, this.getUpdateOfflineFallback(category)) } public deleteCategory(category: Category): Observable { - return this.deleteItem(category, `api/categories/remove?id=${category.id}`); + return this.deleteItem(category, `api/categories/remove?id=${category.id}`, this.getDeleteOfflineFallback(category)); } + + private getUpdateOfflineFallback(category: Category): Observable { + let copy = this.deepClone(category); + copy.state = ModelState.Modified; + return Observable.fromPromise(this._databaseService.categories.put(category).then(()=> { + return true; + }, ()=> { + return false + })); + } + + private getAddOfflineFallback(category: Category): Observable { + let copy = this.deepClone(category); + copy.state = ModelState.New; + copy.id = `-${(new Date()).getTime()}`; + return Observable.fromPromise(this._databaseService.categories.add(copy).then(()=> { + return copy.id; + }, ()=> { + return null + })); + + } + + private getDeleteOfflineFallback(category: Category): Observable { + let copy = this.deepClone(category); + return Observable.fromPromise(this._databaseService.categories.update(copy.id, {state: ModelState.Deleted}).then(()=> { + return true; + }, ()=> { + return false + })); + } + } diff --git a/src/BoardZ/app/services/dashboardService.ts b/src/BoardZ/app/services/dashboardService.ts index 58586ea..562e4b0 100644 --- a/src/BoardZ/app/services/dashboardService.ts +++ b/src/BoardZ/app/services/dashboardService.ts @@ -1,22 +1,39 @@ import {Injectable} from '@angular/core'; import {Observable} from 'rxjs/Observable'; import {AuthenticatedHttp} from './authenticatedHttp'; +import {DatabaseService} from './databaseService'; +import {OfflineDetectionService} from './offlineDetectionService'; +import {ModelState} from '../models/modelState'; @Injectable() export class DashboardService { - constructor(private _http: AuthenticatedHttp) { + constructor(private _http: AuthenticatedHttp, + private _databaseService: DatabaseService, + private _offlineDetectionService: OfflineDetectionService) { } public getGameCount(): Observable { - return this._http.get('api/games/count').map(response => (response.text())); + return Observable.if(()=> { + return this._offlineDetectionService.isOnline + }, + this._http.get('api/games/count').map(response => (response.text())), + Observable.fromPromise(this._databaseService.games.filter(g=>g.state !== ModelState.Deleted).count())); } public getPlayerCount(): Observable { - return this._http.get('api/players/count').map(response => (response.text())); + return Observable.if(()=> { + return this._offlineDetectionService.isOnline + }, + this._http.get('api/players/count').map(response => (response.text())), + Observable.of(0)); } public getCategoryCount(): Observable { - return this._http.get('api/categories/count').map(response => (response.text())); + return Observable.if(()=> { + return this._offlineDetectionService.isOnline + }, + this._http.get('api/categories/count').map(response => (response.text())), + Observable.fromPromise(this._databaseService.categories.filter(c=>c.state !== ModelState.Deleted).count())); } } diff --git a/src/BoardZ/app/services/databaseService.ts b/src/BoardZ/app/services/databaseService.ts new file mode 100644 index 0000000..737da25 --- /dev/null +++ b/src/BoardZ/app/services/databaseService.ts @@ -0,0 +1,23 @@ +import {Injectable} from '@angular/core'; +import Dexie from 'dexie'; +import {Category} from '../models/category'; +import {Game} from '../models/game'; +import {AgeRating} from '../models/ageRating'; + +@Injectable() +export class DatabaseService extends Dexie { + + constructor() { + super("boardzdb"); + this.version(1).stores({ + games: '&id, name, description, userName, state, ageRatingId, rowVersion', + categories: '&id, name, state, numberOfGames, rowVersion', + ageRatings: '&id, name, colorIndicator, state' + }); + + } + + public games: Dexie.Table; + public categories: Dexie.Table; + public ageRatings: Dexie.Table; +} diff --git a/src/BoardZ/app/services/gamesService.ts b/src/BoardZ/app/services/gamesService.ts index fa6e7a4..eb4109d 100644 --- a/src/BoardZ/app/services/gamesService.ts +++ b/src/BoardZ/app/services/gamesService.ts @@ -3,18 +3,22 @@ import {Observable} from 'rxjs/Observable'; import {AuthenticatedHttp} from './authenticatedHttp'; import {Game} from '../models/game'; import {BaseApiService} from './baseApiService'; -import {OfflineStorageService} from './offlineStorageService'; import {OfflineDetectionService} from './offlineDetectionService'; +import {DatabaseService} from './databaseService'; @Injectable() export class GamesService extends BaseApiService { - constructor(http: AuthenticatedHttp, offlineStorageService: OfflineStorageService, offlineDetectionService: OfflineDetectionService) { - super(http, offlineStorageService, offlineDetectionService); + constructor(private _databaseService: DatabaseService, + http: AuthenticatedHttp, + offlineDetectionService: OfflineDetectionService) { + + super(http, _databaseService, offlineDetectionService); super.initializeEntity(Game); + } public getAllGames(): Observable { - return this.getAll('api/games/list'); + return this.getAll('api/games/list', null); } public deepClone(game: Game): Game { @@ -22,18 +26,19 @@ export class GamesService extends BaseApiService { } public getGameById(id: string): Observable { - return this.getSingle(id, `api/games/single?id=${id}`); + return this.getSingle(id, `api/games/single?id=${id}`, null); } public addGame(game: Game): Observable { - return this.add(game, `api/games/add`); + return this.add(game, `api/games/add`, null); } public updateGame(game: Game): Observable { - return this.update(game, `api/games/update`); + return this.update(game, `api/games/update`, null); } public deleteGame(game: Game): Observable { - return this.deleteItem(game, `api/games/remove?id=${game.id}`); + return this.deleteItem(game, `api/games/remove?id=${game.id}`, null); } + } diff --git a/src/BoardZ/app/services/offlineStorageService.ts b/src/BoardZ/app/services/offlineStorageService.ts index f618efc..dfd1699 100644 --- a/src/BoardZ/app/services/offlineStorageService.ts +++ b/src/BoardZ/app/services/offlineStorageService.ts @@ -1,10 +1,16 @@ import {Injectable} from '@angular/core'; import {ISupportsOfflineStorage} from '../interfaces/supportsOfflineStorage'; import {ModelState} from '../models/modelState'; +import {DatabaseService} from './databaseService'; @Injectable() export class OfflineStorageService { + constructor(private _databaseService: DatabaseService){ + + } + + public getAll(): Promise> { return new Promise((resolve, reject) => { resolve([]); diff --git a/src/BoardZ/system.setup.js b/src/BoardZ/system.setup.js index c424807..9d3ee83 100644 --- a/src/BoardZ/system.setup.js +++ b/src/BoardZ/system.setup.js @@ -25,12 +25,14 @@ 'pNotify/pnotify-adapter': 'scripts/bundles/pnotify-adapter.js', 'signalr/signalr': 'scripts/bundles/signalr.js', 'leaflet/leaflet': 'scripts/bundles/leaflet-src.js', - 'fastclick/fastclick': 'scripts/bundles/fastclick.js' + 'fastclick/fastclick': 'scripts/bundles/fastclick.js', + 'dexie': 'dexie/dexie.js' }; // packages tells the System loader how to load when no filename and/or no extension var packages = { 'app': { main: 'main.js', defaultExtension: 'js' }, - 'rxjs': { defaultExtension: 'js' } + 'rxjs': { defaultExtension: 'js' }, + 'dexie': { format: 'amd' } }; var config = { @@ -49,7 +51,6 @@ function backupModule() { window.module = module; module = undefined; } - console.log('mod backuped'); resolve(true); }); } @@ -59,7 +60,6 @@ function restoreModule() { if (window.hasOwnProperty('module')) { module = window.module; } - console.log('mod restored'); resolve(true); }); } From c02c85be09026948886a7bf33e7adb5d45d7c656 Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Tue, 20 Sep 2016 13:56:46 +0200 Subject: [PATCH 37/53] Add some logs for demo --- src/BoardZ/app/services/offlineDetectionService.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/BoardZ/app/services/offlineDetectionService.ts b/src/BoardZ/app/services/offlineDetectionService.ts index 04b6613..cbad624 100644 --- a/src/BoardZ/app/services/offlineDetectionService.ts +++ b/src/BoardZ/app/services/offlineDetectionService.ts @@ -39,21 +39,28 @@ export class OfflineDetectionService { .timeout(this._offlineConfig.absoluteTimeoutAt, Observable.of(this._offlineConfig.absoluteTimeoutAt + 1)) .map(response => (new Date()).getTime() - start) .map(duration => this.getConnectionStateByDuration(duration)) - .catch(() => Observable.of(ConnectionState.Offline)); + .catch(() => { + console.info('=> ConnectionState.Offline (TIMEOUT)'); + return Observable.of(ConnectionState.Offline); + }); } private getConnectionStateByDuration(duration: number): ConnectionState { console.info(`evaluating connection state for ${duration}`); if (duration <= this._offlineConfig.maxDurationForGood) { + console.info('=> ConnectionState.Good'); return ConnectionState.Good; } if (duration <= this._offlineConfig.maxDurationForNormal) { + console.info('=> ConnectionState.Normal'); return ConnectionState.Normal; } if (duration <= this._offlineConfig.maxDurationForToSlow) { + console.info('=> ConnectionState.ToSlow --> handled like offline'); return ConnectionState.ToSlow; } // duration is longer than this._offlineConfig.maxDurationForToSlow + console.info('=> ConnectionState.Offline'); return ConnectionState.Offline; } From 4507c787b0040ae5007b6b933f208a25a13de6b1 Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Tue, 20 Sep 2016 13:57:04 +0200 Subject: [PATCH 38/53] services offline support added --- src/BoardZ/app/services/ageRatingsService.ts | 16 ++++--- src/BoardZ/app/services/baseApiService.ts | 7 ++-- src/BoardZ/app/services/categoriesService.ts | 7 ++-- src/BoardZ/app/services/dashboardService.ts | 12 +++--- src/BoardZ/app/services/gamesService.ts | 44 +++++++++++++++++--- 5 files changed, 57 insertions(+), 29 deletions(-) diff --git a/src/BoardZ/app/services/ageRatingsService.ts b/src/BoardZ/app/services/ageRatingsService.ts index c6e8cee..36887f9 100644 --- a/src/BoardZ/app/services/ageRatingsService.ts +++ b/src/BoardZ/app/services/ageRatingsService.ts @@ -4,23 +4,21 @@ import {AgeRating} from '../models/ageRating'; import {OfflineDetectionService} from './offlineDetectionService'; import {BaseApiService} from './baseApiService'; import {Observable} from 'rxjs/Rx'; +import {DatabaseService} from './databaseService'; @Injectable() export class AgeRatingsService extends BaseApiService { - constructor(http: AuthenticatedHttp, offlineDetectionService: OfflineDetectionService) { + constructor(http: AuthenticatedHttp, + offlineDetectionService: OfflineDetectionService, + private _databaseService: DatabaseService) { super(http, offlineDetectionService); super.initializeEntity(AgeRating); } - public initialize(): void { - this.getAllAgeRatings() - .subscribe(() => { - //todo: store those - }); - } - public getAllAgeRatings(): Observable> { - return this.getAll('api/ageratings/list', null); + return this.getAll('api/ageratings/list', + this._databaseService.ageRatings.bulkAdd, + Observable.fromPromise(this._databaseService.ageRatings.toArray())); } } diff --git a/src/BoardZ/app/services/baseApiService.ts b/src/BoardZ/app/services/baseApiService.ts index 7a0aef4..bd5d99d 100644 --- a/src/BoardZ/app/services/baseApiService.ts +++ b/src/BoardZ/app/services/baseApiService.ts @@ -3,7 +3,7 @@ import {AuthenticatedHttp} from './authenticatedHttp'; import {Observable} from 'rxjs/Rx'; import {OfflineDetectionService} from './offlineDetectionService'; import {ISupportsOfflineStorage} from '../interfaces/supportsOfflineStorage'; -import {DatabaseService} from './databaseService'; +import {Dexie} from 'dexie/dist/dexie'; @Injectable() export abstract class BaseApiService { @@ -11,7 +11,6 @@ export abstract class BaseApiService { private _entityType: Type; constructor(private _authenticatedHttp: AuthenticatedHttp, - private _databaseService: DatabaseService, private _offlineDetectionService: OfflineDetectionService) { } @@ -22,12 +21,12 @@ export abstract class BaseApiService { /** * return all items of the generic type */ - protected getAll(url: string, offlineFallback: Observable>): Observable> { + protected getAll(url: string, table: Dexie.Table, offlineFallback: Observable>): Observable> { let httpObservable: Observable> = this._authenticatedHttp.get(url) .map(response => response.json()) .map(rawJsonResults => rawJsonResults.map(rawJsonResult => (new this._entityType()).fromRawJson(rawJsonResult))) - .do((results) => this._databaseService.categories.bulkAdd(results).then(()=>results)); + .do((results) => table.bulkPut(results).then(()=>results)); return Observable.if(()=> { return this._offlineDetectionService.isOnline; diff --git a/src/BoardZ/app/services/categoriesService.ts b/src/BoardZ/app/services/categoriesService.ts index e22f43d..6d61cb6 100644 --- a/src/BoardZ/app/services/categoriesService.ts +++ b/src/BoardZ/app/services/categoriesService.ts @@ -13,7 +13,7 @@ export class CategoriesService extends BaseApiService { constructor(private _databaseService: DatabaseService, http: AuthenticatedHttp, offlineDetectionService: OfflineDetectionService) { - super(http, _databaseService, offlineDetectionService); + super(http, offlineDetectionService); super.initializeEntity(Category); } @@ -23,8 +23,7 @@ export class CategoriesService extends BaseApiService { } public getAllCategories(): Observable> { - let offlineFallback = Observable.fromPromise(this._databaseService.categories.filter(c=>c.state !== ModelState.Deleted).toArray()); - return this.getAll('api/categories/list', offlineFallback); + return this.getAll('api/categories/list', this._databaseService.categories, Observable.fromPromise(this._databaseService.categories.filter(c=>c.state !== ModelState.Deleted).toArray())); } public getCategoryById(id: string): Observable { @@ -68,7 +67,7 @@ export class CategoriesService extends BaseApiService { private getDeleteOfflineFallback(category: Category): Observable { let copy = this.deepClone(category); - return Observable.fromPromise(this._databaseService.categories.update(copy.id, {state: ModelState.Deleted}).then(()=> { + return Observable.fromPromise(this._databaseService.categories.update(copy.id, { state: ModelState.Deleted }).then(()=> { return true; }, ()=> { return false diff --git a/src/BoardZ/app/services/dashboardService.ts b/src/BoardZ/app/services/dashboardService.ts index 562e4b0..1a8a9c7 100644 --- a/src/BoardZ/app/services/dashboardService.ts +++ b/src/BoardZ/app/services/dashboardService.ts @@ -12,27 +12,27 @@ export class DashboardService { private _offlineDetectionService: OfflineDetectionService) { } - public getGameCount(): Observable { + public getGameCount(): Observable { return Observable.if(()=> { return this._offlineDetectionService.isOnline }, - this._http.get('api/games/count').map(response => (response.text())), + this._http.get('api/games/count').map(response => (response.text())).catch(()=>Observable.of('-')), Observable.fromPromise(this._databaseService.games.filter(g=>g.state !== ModelState.Deleted).count())); } - public getPlayerCount(): Observable { + public getPlayerCount(): Observable { return Observable.if(()=> { return this._offlineDetectionService.isOnline }, - this._http.get('api/players/count').map(response => (response.text())), + this._http.get('api/players/count').map(response => (response.text())).catch(()=>Observable.of('-')), Observable.of(0)); } - public getCategoryCount(): Observable { + public getCategoryCount(): Observable { return Observable.if(()=> { return this._offlineDetectionService.isOnline }, - this._http.get('api/categories/count').map(response => (response.text())), + this._http.get('api/categories/count').map(response => (response.text())).catch(()=>Observable.of('-')), Observable.fromPromise(this._databaseService.categories.filter(c=>c.state !== ModelState.Deleted).count())); } diff --git a/src/BoardZ/app/services/gamesService.ts b/src/BoardZ/app/services/gamesService.ts index eb4109d..6df6816 100644 --- a/src/BoardZ/app/services/gamesService.ts +++ b/src/BoardZ/app/services/gamesService.ts @@ -5,6 +5,7 @@ import {Game} from '../models/game'; import {BaseApiService} from './baseApiService'; import {OfflineDetectionService} from './offlineDetectionService'; import {DatabaseService} from './databaseService'; +import {ModelState} from '../models/modelState'; @Injectable() export class GamesService extends BaseApiService { @@ -12,13 +13,13 @@ export class GamesService extends BaseApiService { http: AuthenticatedHttp, offlineDetectionService: OfflineDetectionService) { - super(http, _databaseService, offlineDetectionService); + super(http, offlineDetectionService); super.initializeEntity(Game); } public getAllGames(): Observable { - return this.getAll('api/games/list', null); + return this.getAll('api/games/list', this._databaseService.games, Observable.fromPromise(this._databaseService.games.filter(g => g.state !== ModelState.Deleted).toArray())) } public deepClone(game: Game): Game { @@ -26,19 +27,50 @@ export class GamesService extends BaseApiService { } public getGameById(id: string): Observable { - return this.getSingle(id, `api/games/single?id=${id}`, null); + return this.getSingle(id, `api/games/single?id=${id}`, Observable.fromPromise(this._databaseService.games.get(id))); } public addGame(game: Game): Observable { - return this.add(game, `api/games/add`, null); + return this.add(game, `api/games/add`, this.getAddOfflineFallback(game)); } public updateGame(game: Game): Observable { - return this.update(game, `api/games/update`, null); + return this.update(game, `api/games/update`, this.getUpdateOfflineFallback(game)); } public deleteGame(game: Game): Observable { - return this.deleteItem(game, `api/games/remove?id=${game.id}`, null); + return this.deleteItem(game, `api/games/remove?id=${game.id}`, this.getDeleteOfflineFallback(game)); + } + + private getUpdateOfflineFallback(game: Game): Observable { + let copy = this.deepClone(game); + copy.state = ModelState.Modified; + return Observable.fromPromise(this._databaseService.games.put(game).then(()=> { + return true; + }, ()=> { + return false + })); + } + + private getAddOfflineFallback(game: Game): Observable { + let copy = this.deepClone(game); + copy.state = ModelState.New; + copy.id = `-${(new Date()).getTime()}`; + return Observable.fromPromise(this._databaseService.games.add(copy).then(()=> { + return copy.id; + }, ()=> { + return null + })); + + } + + private getDeleteOfflineFallback(game: Game): Observable { + let copy = this.deepClone(game); + return Observable.fromPromise(this._databaseService.games.update(copy.id, {state: ModelState.Deleted}).then(()=> { + return true; + }, ()=> { + return false + })); } } From 0395592766c6bda0691f7c67be8ce181bdf5f8c8 Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Tue, 20 Sep 2016 13:57:25 +0200 Subject: [PATCH 39/53] usge getAllAgeRatings instead of initialize --- src/BoardZ/app/components/login/login.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/BoardZ/app/components/login/login.ts b/src/BoardZ/app/components/login/login.ts index 2b3c423..49dea85 100644 --- a/src/BoardZ/app/components/login/login.ts +++ b/src/BoardZ/app/components/login/login.ts @@ -21,8 +21,7 @@ export class LoginComponent { private _logService: LogService, private _notificationService: NotificationService, private _signalRService: SignalRService, - private _ageRatingsService: AgeRatingsService - ) { + private _ageRatingsService: AgeRatingsService) { } public doLogin(): void { @@ -31,8 +30,7 @@ export class LoginComponent { this._loginService.login(this._userName, this._password) .subscribe( () => { - this._ageRatingsService.initialize(); - + this._ageRatingsService.getAllAgeRatings(); this._signalRService.start(); this.setError(false); this._router.navigate(['']); From 74879a7214260181991ca5659431bf60b79ead47 Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Tue, 20 Sep 2016 13:57:42 +0200 Subject: [PATCH 40/53] removed unused logService --- src/BoardZ/app/components/games/details.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/BoardZ/app/components/games/details.ts b/src/BoardZ/app/components/games/details.ts index d4dc6c1..9d77c35 100644 --- a/src/BoardZ/app/components/games/details.ts +++ b/src/BoardZ/app/components/games/details.ts @@ -1,7 +1,6 @@ import {Component, OnInit} from '@angular/core'; import {Router, ActivatedRoute} from '@angular/router'; import {Game} from '../../models/game'; -import {LogService} from '../../services/logService'; import {GamesService} from '../../services/gamesService'; import {NotificationService} from '../../services/notificationService'; import {SignalRService} from '../../services/signalrService'; @@ -34,10 +33,9 @@ export class GameDetailsComponent implements OnInit { public originalModel: Game = new Game(); public selectedCategories: Array = []; - constructor(private _logService: LogService, - private _gameService: GamesService, - private _router: Router, + constructor(private _router: Router, private route: ActivatedRoute, + private _gameService: GamesService, private _notificationService: NotificationService, private _categoriesService: CategoriesService, private _ageRatingsService: AgeRatingsService, From 2358c67f00e73d75a4cf9ef5881e0efe7d236cf9 Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Tue, 20 Sep 2016 14:08:55 +0200 Subject: [PATCH 41/53] =?UTF-8?q?Don=E2=80=99t=20let=20user=20play=20if=20?= =?UTF-8?q?he/she=20is=20online?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BoardZ/app/components/games/details.html | 2 +- src/BoardZ/app/components/games/details.ts | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/BoardZ/app/components/games/details.html b/src/BoardZ/app/components/games/details.html index 10e160b..25b70e8 100644 --- a/src/BoardZ/app/components/games/details.html +++ b/src/BoardZ/app/components/games/details.html @@ -63,7 +63,7 @@

Game details

-
+
diff --git a/src/BoardZ/app/components/games/details.ts b/src/BoardZ/app/components/games/details.ts index 9d77c35..897f67d 100644 --- a/src/BoardZ/app/components/games/details.ts +++ b/src/BoardZ/app/components/games/details.ts @@ -14,6 +14,7 @@ import {AgeRating} from '../../models/ageRating'; import {AgeRatingsService} from '../../services/ageRatingsService'; import {Category} from '../../models/category'; import {CategoriesService} from '../../services/categoriesService'; +import {OfflineDetectionService} from '../../services/offlineDetectionService'; @Component({ moduleId: module.id, @@ -41,6 +42,7 @@ export class GameDetailsComponent implements OnInit { private _ageRatingsService: AgeRatingsService, private _playersService: PlayersService, private _signalRService: SignalRService, + private _offlineDetectionService: OfflineDetectionService, private _loginService: LoginService) { } @@ -127,6 +129,10 @@ export class GameDetailsComponent implements OnInit { this._pictureUrl = pictureUrl; } + public get isOnline(): boolean{ + return this._offlineDetectionService.isOnline; + } + public canPlay() { return this._coordinates && this._pictureUrl; } From bc8bb8547d2e0d651dc5194933be438b2f9a2d5a Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Tue, 20 Sep 2016 14:09:36 +0200 Subject: [PATCH 42/53] =?UTF-8?q?fallback=20=E2=80=98numberOfGames?= =?UTF-8?q?=E2=80=99=20to=20=E2=80=980=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BoardZ/app/components/categories/list.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BoardZ/app/components/categories/list.html b/src/BoardZ/app/components/categories/list.html index 4d43abf..b112ad8 100644 --- a/src/BoardZ/app/components/categories/list.html +++ b/src/BoardZ/app/components/categories/list.html @@ -21,7 +21,7 @@

All Categories

{{cat.name}} - {{cat.numberOfGames}} + {{cat.numberOfGames || '0'}} From f918a3e43d5432b966d838ba0c6d039ec51843c3 Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Tue, 20 Sep 2016 14:53:56 +0200 Subject: [PATCH 43/53] colorize the boardz-header depending on the ConnectionState --- src/BoardZ/app/app.ts | 4 ++- .../directives/offlineIndicatorDirective.ts | 33 +++++++++++++++++++ src/BoardZ/app/modules/config.ts | 4 ++- .../app/services/offlineDetectionService.ts | 2 ++ src/BoardZ/css/app.css | 4 +++ src/BoardZ/index.html | 2 +- 6 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 src/BoardZ/app/directives/offlineIndicatorDirective.ts diff --git a/src/BoardZ/app/app.ts b/src/BoardZ/app/app.ts index 8047c1f..5c7bdf8 100644 --- a/src/BoardZ/app/app.ts +++ b/src/BoardZ/app/app.ts @@ -8,12 +8,13 @@ import {UiNotificationService} from './services/uiNotificationService'; import {NativeIntegrationService} from './services/nativeIntegrationService'; import {IBoardZAppWindow} from './interfaces/boardzAppWindow'; import {OfflineDetectionService} from './services/offlineDetectionService'; +import {ConnectionState} from './models/connectionState'; declare var window: IBoardZAppWindow; @Component({ moduleId: module.id, - selector: 'boardz-app', + selector: 'body', templateUrl: 'app.html' }) @@ -33,6 +34,7 @@ export class BoardzAppComponent implements OnInit, AfterViewInit, OnDestroy { this._offlineDetectionService.startConnectionMonitoring(); } + public ngAfterViewInit(): any { if (window.initAdminLTE) { window.initAdminLTE(); diff --git a/src/BoardZ/app/directives/offlineIndicatorDirective.ts b/src/BoardZ/app/directives/offlineIndicatorDirective.ts new file mode 100644 index 0000000..615434f --- /dev/null +++ b/src/BoardZ/app/directives/offlineIndicatorDirective.ts @@ -0,0 +1,33 @@ +import {Directive} from '@angular/core'; +import {OfflineDetectionService} from '../services/offlineDetectionService'; +import {ConnectionState} from '../models/connectionState'; + +@Directive({ + selector: 'boardz-header' +}) +export class OfflineIndicatorDirective { + + constructor(private _offlineDetectionService: OfflineDetectionService) { + this._offlineDetectionService.connectionChanged.asObservable().subscribe((connectionState)=> { + switch (connectionState) { + case ConnectionState.Offline: + document.body.classList.remove('skin-blue', 'skin-red', 'skin-yellow'); + document.body.classList.add('skin-red'); + break; + case ConnectionState.Initializing: + document.body.classList.remove('skin-blue', 'skin-red', 'skin-yellow'); + document.body.classList.add('skin-yellow'); + break; + case ConnectionState.Normal: + document.body.classList.remove('skin-blue', 'skin-red', 'skin-yellow'); + document.body.classList.add('skin-blue'); + break; + case ConnectionState.Good: + document.body.classList.remove('skin-blue', 'skin-red', 'skin-yellow'); + document.body.classList.add('skin-blue'); + break; + } + }); + } + +} diff --git a/src/BoardZ/app/modules/config.ts b/src/BoardZ/app/modules/config.ts index 20748f9..b77b9e1 100644 --- a/src/BoardZ/app/modules/config.ts +++ b/src/BoardZ/app/modules/config.ts @@ -48,6 +48,7 @@ import {OfflineStorageService} from '../services/offlineStorageService'; import {Game} from '../models/game'; import {Category} from '../models/category'; import {DatabaseService} from '../services/databaseService'; +import {OfflineIndicatorDirective} from '../directives/offlineIndicatorDirective'; export namespace ModuleConfiguration { @@ -63,7 +64,8 @@ export namespace ModuleConfiguration { SidebarComponent, WidgetComponent, BackButtonDirective, - CloseSidebarOnClickDirective + CloseSidebarOnClickDirective, + OfflineIndicatorDirective ]; public static imports = [ diff --git a/src/BoardZ/app/services/offlineDetectionService.ts b/src/BoardZ/app/services/offlineDetectionService.ts index cbad624..2fe925b 100644 --- a/src/BoardZ/app/services/offlineDetectionService.ts +++ b/src/BoardZ/app/services/offlineDetectionService.ts @@ -20,6 +20,7 @@ export class OfflineDetectionService { } public connectionRestoring: Subject = new Subject(); + public connectionChanged: Subject = new Subject(); private get pingUrl(): string { return `${this._apiConfig.rootUrl}api/status/ping`; @@ -72,6 +73,7 @@ export class OfflineDetectionService { this.connectionRestoring.next(state); } this._recentConnectionState = state; + this.connectionChanged.next(state); }); }, this._offlineConfig.checkInterval); } diff --git a/src/BoardZ/css/app.css b/src/BoardZ/css/app.css index 1f9159c..5d3abf2 100644 --- a/src/BoardZ/css/app.css +++ b/src/BoardZ/css/app.css @@ -62,3 +62,7 @@ body, overflow-y: scroll; -webkit-overflow-scrolling: touch; } + +.ui-pnotify{ + marign-top:100px; +} diff --git a/src/BoardZ/index.html b/src/BoardZ/index.html index e1675b0..1b5f5d4 100644 --- a/src/BoardZ/index.html +++ b/src/BoardZ/index.html @@ -8,7 +8,7 @@ - +
From 24f552425a6c00bee7434a19ca1e7f82dad46239 Mon Sep 17 00:00:00 2001 From: Thorsten Hans Date: Wed, 21 Sep 2016 17:00:24 +0200 Subject: [PATCH 44/53] SyncService and restore connection behavior --- src/BoardZ/app/app.ts | 2 + .../app/components/dashboard/dashboard.ts | 15 +++- src/BoardZ/app/components/games/details.ts | 4 +- src/BoardZ/app/components/header/header.html | 6 +- src/BoardZ/app/components/header/header.ts | 26 ++++++- src/BoardZ/app/components/login/login.ts | 2 +- src/BoardZ/app/modules/config.ts | 8 +-- src/BoardZ/app/services/ageRatingsService.ts | 8 ++- src/BoardZ/app/services/baseApiService.ts | 6 +- src/BoardZ/app/services/gamesService.ts | 2 +- .../app/services/offlineDetectionService.ts | 36 +++++++++- .../app/services/offlineStorageService.ts | 43 ----------- src/BoardZ/app/services/syncService.ts | 72 +++++++++++++++++++ src/BoardZ/index.html | 2 +- src/BoardZApi/BoardGame.Api/BoardzContext.cs | 4 +- .../BoardzDatabaseConfiguration.cs | 2 +- .../Controllers/GamesController.cs | 6 +- .../BoardGame.Api/Models/Category.cs | 6 +- src/BoardZApi/BoardGame.Api/Models/Game.cs | 5 +- .../BoardGame.Api/Models/ModelState.cs | 1 + .../BoardGame.Api/Services/GamesService.cs | 7 +- src/BoardZApi/docs/BoardGame.Api.xml | 8 +-- 22 files changed, 189 insertions(+), 82 deletions(-) delete mode 100644 src/BoardZ/app/services/offlineStorageService.ts create mode 100644 src/BoardZ/app/services/syncService.ts diff --git a/src/BoardZ/app/app.ts b/src/BoardZ/app/app.ts index 5c7bdf8..cc136e8 100644 --- a/src/BoardZ/app/app.ts +++ b/src/BoardZ/app/app.ts @@ -9,6 +9,7 @@ import {NativeIntegrationService} from './services/nativeIntegrationService'; import {IBoardZAppWindow} from './interfaces/boardzAppWindow'; import {OfflineDetectionService} from './services/offlineDetectionService'; import {ConnectionState} from './models/connectionState'; +import {SyncService} from './services/syncService'; declare var window: IBoardZAppWindow; @@ -23,6 +24,7 @@ export class BoardzAppComponent implements OnInit, AfterViewInit, OnDestroy { private _loginService: LoginService, private _notificationService: NotificationService, private _offlineDetectionService: OfflineDetectionService, + private _syncService: SyncService, private _nativeIntegrationService: NativeIntegrationService, private _uiNotificationService: UiNotificationService, private _logService: LogService) { diff --git a/src/BoardZ/app/components/dashboard/dashboard.ts b/src/BoardZ/app/components/dashboard/dashboard.ts index 5e6c9f9..254c6db 100644 --- a/src/BoardZ/app/components/dashboard/dashboard.ts +++ b/src/BoardZ/app/components/dashboard/dashboard.ts @@ -1,5 +1,7 @@ import {Component, OnInit} from '@angular/core'; import {DashboardService} from '../../services/dashboardService'; +import {OfflineDetectionService} from '../../services/offlineDetectionService'; +import {TokenService} from '../../services/tokenService'; @Component({ moduleId: module.id, @@ -11,10 +13,21 @@ export class DashboardComponent implements OnInit { public gameCount: string = '-'; public categoryCount: string = '-'; - constructor(private _dashboardService: DashboardService) { + constructor(private _dashboardService: DashboardService, + private _offlineDetectionService: OfflineDetectionService, + private _tokenService: TokenService) { } public ngOnInit(): any { + this.getDashboardData(); + this._offlineDetectionService.connectionRestoring.asObservable().subscribe(()=> { + if(this._tokenService.isAuthenticated()) { + this.getDashboardData(); + } + }); + } + + private getDashboardData() { this._dashboardService.getPlayerCount() .subscribe(result => this.playerCount = result.toString()); diff --git a/src/BoardZ/app/components/games/details.ts b/src/BoardZ/app/components/games/details.ts index 897f67d..ccd5008 100644 --- a/src/BoardZ/app/components/games/details.ts +++ b/src/BoardZ/app/components/games/details.ts @@ -48,7 +48,9 @@ export class GameDetailsComponent implements OnInit { public ngOnInit(): any { this._categoriesService.getAllCategories().subscribe(cats=>this.categories = cats); - this._ageRatingsService.getAllAgeRatings().subscribe(ars=>this.ageRatings = ars); + this._ageRatingsService.getOfflineAgeRatings().subscribe((ars)=>{ + this.ageRatings = ars; + }); this.route.data.forEach((data: { game: Game }) => { this.originalModel = this._gameService.deepClone(this.model = data.game || new Game()); diff --git a/src/BoardZ/app/components/header/header.html b/src/BoardZ/app/components/header/header.html index 20479ee..317d3cd 100644 --- a/src/BoardZ/app/components/header/header.html +++ b/src/BoardZ/app/components/header/header.html @@ -17,11 +17,7 @@
  • - +