diff --git a/QRBee.Core/Client/APIClient.nswag b/QRBee.Core/Client/APIClient.nswag index 831c79a..4dee2bb 100644 --- a/QRBee.Core/Client/APIClient.nswag +++ b/QRBee.Core/Client/APIClient.nswag @@ -3,7 +3,7 @@ "defaultVariables": null, "documentGenerator": { "fromDocument": { - "json": "{\r\n \"openapi\": \"3.0.1\",\r\n \"info\": {\r\n \"title\": \"QRBee.Api\",\r\n \"version\": \"1.0\"\r\n },\r\n \"paths\": {\r\n \"/api/QRBee/Register\": {\r\n \"post\": {\r\n \"tags\": [\r\n \"QRBee\"\r\n ],\r\n \"requestBody\": {\r\n \"content\": {\r\n \"application/json\": {\r\n \"schema\": {\r\n \"$ref\": \"#/components/schemas/RegistrationRequest\"\r\n }\r\n },\r\n \"text/json\": {\r\n \"schema\": {\r\n \"$ref\": \"#/components/schemas/RegistrationRequest\"\r\n }\r\n },\r\n \"application/*+json\": {\r\n \"schema\": {\r\n \"$ref\": \"#/components/schemas/RegistrationRequest\"\r\n }\r\n }\r\n }\r\n },\r\n \"responses\": {\r\n \"200\": {\r\n \"description\": \"Success\",\r\n \"content\": {\r\n \"text/plain\": {\r\n \"schema\": {\r\n \"$ref\": \"#/components/schemas/RegistrationResponse\"\r\n }\r\n },\r\n \"application/json\": {\r\n \"schema\": {\r\n \"$ref\": \"#/components/schemas/RegistrationResponse\"\r\n }\r\n },\r\n \"text/json\": {\r\n \"schema\": {\r\n \"$ref\": \"#/components/schemas/RegistrationResponse\"\r\n }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n },\r\n \"components\": {\r\n \"schemas\": {\r\n \"RegistrationRequest\": {\r\n \"type\": \"object\",\r\n \"properties\": {\r\n \"name\": {\r\n \"type\": \"string\",\r\n \"nullable\": true\r\n },\r\n \"email\": {\r\n \"type\": \"string\",\r\n \"nullable\": true\r\n },\r\n \"dateOfBirth\": {\r\n \"type\": \"string\",\r\n \"nullable\": true\r\n },\r\n \"certificateRequest\": {\r\n \"type\": \"string\",\r\n \"nullable\": true\r\n },\r\n \"registerAsMerchant\": {\r\n \"type\": \"boolean\"\r\n }\r\n },\r\n \"additionalProperties\": false\r\n },\r\n \"RegistrationResponse\": {\r\n \"type\": \"object\",\r\n \"properties\": {\r\n \"clientId\": {\r\n \"type\": \"string\",\r\n \"nullable\": true\r\n },\r\n \"certificate\": {\r\n \"type\": \"string\",\r\n \"nullable\": true\r\n }\r\n },\r\n \"additionalProperties\": false\r\n }\r\n }\r\n }\r\n}", + "json": "{\r\n \"openapi\": \"3.0.1\",\r\n \"info\": {\r\n \"title\": \"QRBee.Api\",\r\n \"version\": \"1.0\"\r\n },\r\n \"paths\": {\r\n \"/api/QRBee/Register\": {\r\n \"post\": {\r\n \"tags\": [\r\n \"QRBee\"\r\n ],\r\n \"requestBody\": {\r\n \"content\": {\r\n \"application/json\": {\r\n \"schema\": {\r\n \"$ref\": \"#/components/schemas/RegistrationRequest\"\r\n }\r\n },\r\n \"text/json\": {\r\n \"schema\": {\r\n \"$ref\": \"#/components/schemas/RegistrationRequest\"\r\n }\r\n },\r\n \"application/*+json\": {\r\n \"schema\": {\r\n \"$ref\": \"#/components/schemas/RegistrationRequest\"\r\n }\r\n }\r\n }\r\n },\r\n \"responses\": {\r\n \"200\": {\r\n \"description\": \"Success\",\r\n \"content\": {\r\n \"text/plain\": {\r\n \"schema\": {\r\n \"$ref\": \"#/components/schemas/RegistrationResponse\"\r\n }\r\n },\r\n \"application/json\": {\r\n \"schema\": {\r\n \"$ref\": \"#/components/schemas/RegistrationResponse\"\r\n }\r\n },\r\n \"text/json\": {\r\n \"schema\": {\r\n \"$ref\": \"#/components/schemas/RegistrationResponse\"\r\n }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n },\r\n \"/api/QRBee/Update/{clientId}\": {\r\n \"patch\": {\r\n \"tags\": [\r\n \"QRBee\"\r\n ],\r\n \"parameters\": [\r\n {\r\n \"name\": \"clientId\",\r\n \"in\": \"path\",\r\n \"required\": true,\r\n \"schema\": {\r\n \"type\": \"string\"\r\n }\r\n }\r\n ],\r\n \"requestBody\": {\r\n \"content\": {\r\n \"application/json\": {\r\n \"schema\": {\r\n \"$ref\": \"#/components/schemas/RegistrationRequest\"\r\n }\r\n },\r\n \"text/json\": {\r\n \"schema\": {\r\n \"$ref\": \"#/components/schemas/RegistrationRequest\"\r\n }\r\n },\r\n \"application/*+json\": {\r\n \"schema\": {\r\n \"$ref\": \"#/components/schemas/RegistrationRequest\"\r\n }\r\n }\r\n }\r\n },\r\n \"responses\": {\r\n \"200\": {\r\n \"description\": \"Success\"\r\n }\r\n }\r\n }\r\n }\r\n },\r\n \"components\": {\r\n \"schemas\": {\r\n \"RegistrationRequest\": {\r\n \"type\": \"object\",\r\n \"properties\": {\r\n \"name\": {\r\n \"type\": \"string\",\r\n \"nullable\": true\r\n },\r\n \"email\": {\r\n \"type\": \"string\",\r\n \"nullable\": true\r\n },\r\n \"dateOfBirth\": {\r\n \"type\": \"string\",\r\n \"nullable\": true\r\n },\r\n \"certificateRequest\": {\r\n \"type\": \"string\",\r\n \"nullable\": true\r\n },\r\n \"registerAsMerchant\": {\r\n \"type\": \"boolean\"\r\n }\r\n },\r\n \"additionalProperties\": false\r\n },\r\n \"RegistrationResponse\": {\r\n \"type\": \"object\",\r\n \"properties\": {\r\n \"clientId\": {\r\n \"type\": \"string\",\r\n \"nullable\": true\r\n },\r\n \"certificate\": {\r\n \"type\": \"string\",\r\n \"nullable\": true\r\n }\r\n },\r\n \"additionalProperties\": false\r\n }\r\n }\r\n }\r\n}", "url": "http://redocly.github.io/redoc/openapi.yaml", "output": null, "newLineBehavior": "Auto" diff --git a/QRBee.Core/Client/Client.cs b/QRBee.Core/Client/Client.cs index 4df7dc1..9a1b912 100644 --- a/QRBee.Core/Client/Client.cs +++ b/QRBee.Core/Client/Client.cs @@ -130,6 +130,81 @@ namespace QRBee.Core.Client } } + /// Success + /// A server side error occurred. + public virtual System.Threading.Tasks.Task UpdateAsync(string clientId, RegistrationRequest body) + { + return UpdateAsync(clientId, body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Success + /// A server side error occurred. + public virtual async System.Threading.Tasks.Task UpdateAsync(string clientId, RegistrationRequest body, System.Threading.CancellationToken cancellationToken) + { + if (clientId == null) + throw new System.ArgumentNullException("clientId"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/QRBee/Update/{clientId}"); + urlBuilder_.Replace("{clientId}", System.Uri.EscapeDataString(ConvertToString(clientId, System.Globalization.CultureInfo.InvariantCulture))); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("PATCH"); + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + return; + } + else + { + var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); + throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + protected struct ObjectResponseResult { public ObjectResponseResult(T responseObject, string responseText) diff --git a/QRBee/QRBee/Services/ILocalSettings.cs b/QRBee/QRBee/Services/ILocalSettings.cs index 37ead0c..b1ebbcf 100644 --- a/QRBee/QRBee/Services/ILocalSettings.cs +++ b/QRBee/QRBee/Services/ILocalSettings.cs @@ -8,8 +8,9 @@ namespace QRBee.Services public class Settings { //TODO add ClientId + public string ClientId { get; set; } public string PIN { get; set; } - public bool IsRegistered { get; set; } + public bool IsRegistered => !string.IsNullOrWhiteSpace(ClientId); public string Name { get; set; } public string Email { get; set; } diff --git a/QRBee/QRBee/ViewModels/RegisterViewModel.cs b/QRBee/QRBee/ViewModels/RegisterViewModel.cs index 9306630..f48ac53 100644 --- a/QRBee/QRBee/ViewModels/RegisterViewModel.cs +++ b/QRBee/QRBee/ViewModels/RegisterViewModel.cs @@ -102,34 +102,50 @@ namespace QRBee.ViewModels try { //TODO Check if ClientId already in LocalSettings. If Yes update data in database + var settings = localSettings.LoadSettings(); //save local settings - var settings = new Settings - { - CardHolderName = CardHolderName, - CardNumber = CardNumber, - CVC = CVC, - DateOfBirth = DateOfBirth, - Email = Email, - ExpirationDate = ExpirationDate, - IsRegistered = true, - IssueNo = IssueNo, - ValidFrom = ValidFrom, - Name = Name, - PIN = Pin - }; + settings.CardHolderName = CardHolderName; + settings.CardNumber = CardNumber; + settings.CVC = CVC; + settings.DateOfBirth = DateOfBirth; + settings.Email = Email; + settings.ExpirationDate = ExpirationDate; + settings.IssueNo = IssueNo; + settings.ValidFrom = ValidFrom; + settings.Name = Name; + settings.PIN = Pin; + await localSettings.SaveSettings(settings); - await service.RegisterAsync(new RegistrationRequest + var request = new RegistrationRequest { - DateOfBirth = DateOfBirth.ToString("yyyy-MM-dd"), - Email = Email, - Name = Name, + DateOfBirth = DateOfBirth.ToString("yyyy-MM-dd"), + Email = Email, + Name = Name, RegisterAsMerchant = false - }); + }; + + if (!settings.IsRegistered) + { + var response = await service.RegisterAsync(request); + + // Save ClientId to LocalSettings + settings = localSettings.LoadSettings(); + settings.ClientId = response.ClientId; + await localSettings.SaveSettings(settings); + + var page = Application.Current.MainPage.Navigation.NavigationStack.LastOrDefault(); + await page.DisplayAlert("Success", "You have been registered successfully", "Ok"); + } + else + { + await service.UpdateAsync(settings.ClientId, request); + var page = Application.Current.MainPage.Navigation.NavigationStack.LastOrDefault(); + await page.DisplayAlert("Success", "Your data has been updated successfully", "Ok"); + } + - var page = Application.Current.MainPage.Navigation.NavigationStack.LastOrDefault(); - await page.DisplayAlert("Success", "You have been registered successfully", "Ok"); await Shell.Current.GoToAsync($"//{nameof(MainPage)}"); } diff --git a/QRBeeApi/Controllers/QRBeeController.cs b/QRBeeApi/Controllers/QRBeeController.cs index 0ca1031..b93fd37 100644 --- a/QRBeeApi/Controllers/QRBeeController.cs +++ b/QRBeeApi/Controllers/QRBeeController.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Mvc; +using MongoDB.Driver; using QRBee.Api.Services; using QRBee.Core; using QRBee.Core.Data; @@ -27,5 +28,11 @@ namespace QRBee.Api.Controllers return _service.Register(value); } + [HttpPatch("Update/{clientId}")] + public Task Update([FromRoute] string clientId, [FromBody] RegistrationRequest value) + { + _logger.LogInformation($"Trying to update user {value.Name}"); + return _service.Update(clientId,value); + } } } diff --git a/QRBeeApi/Services/Database/IStorage.cs b/QRBeeApi/Services/Database/IStorage.cs index a37ae86..b362113 100644 --- a/QRBeeApi/Services/Database/IStorage.cs +++ b/QRBeeApi/Services/Database/IStorage.cs @@ -19,5 +19,7 @@ /// User information Task GetUserInfo(string email); + Task UpdateUser(UserInfo info); + } } diff --git a/QRBeeApi/Services/Database/Storage.cs b/QRBeeApi/Services/Database/Storage.cs index 6cb6b03..df59218 100644 --- a/QRBeeApi/Services/Database/Storage.cs +++ b/QRBeeApi/Services/Database/Storage.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.Options; using MongoDB.Driver; +using QRBee.Api.Controllers; namespace QRBee.Api.Services.Database { @@ -7,12 +8,14 @@ namespace QRBee.Api.Services.Database { private readonly IMongoDatabase _database; + private readonly ILogger _logger; - public Storage(IMongoClient client, IOptions settings) + public Storage(IMongoClient client, IOptions settings, ILogger logger) { var name = settings.Value.DatabaseName; _database = client.GetDatabase(name); + _logger = logger; } public async Task PutUserInfo(UserInfo info) @@ -27,13 +30,20 @@ namespace QRBee.Api.Services.Database if (user == null) { await collection.InsertOneAsync(info); + _logger.LogInformation($"Inserted new user with Email: {info.Email} and ID: {info.ClientId}"); return info.ClientId ?? throw new ApplicationException($"ClientId is null while adding user {info.Email}"); } + + // Update command will not be used due to not knowing if the user is legitimate + _logger.LogInformation($"Found user with Email: {info.Email} and ID: {info.ClientId}"); return user.ClientId ?? throw new ApplicationException($"ClientId is null while adding user {info.Email}"); - - } + /// + /// Check if user already exists in database + /// + /// Parameter by which to find UserInfo + /// null if user doesn't exist or UserInfo internal async Task TryGetUserInfo(string email) { var collection = _database.GetCollection("Users"); @@ -52,5 +62,11 @@ namespace QRBee.Api.Services.Database return user ?? throw new ApplicationException($"User {email} not found."); } + public async Task UpdateUser(UserInfo info) + { + var collection = _database.GetCollection("Users"); + await collection.ReplaceOneAsync($"{{ _id: \"{info.Id}\" }}",info, new ReplaceOptions(){IsUpsert = false}); + } + } } diff --git a/QRBeeApi/Services/IQRBeeAPI.cs b/QRBeeApi/Services/IQRBeeAPI.cs index 5bc568c..cf64ebf 100644 --- a/QRBeeApi/Services/IQRBeeAPI.cs +++ b/QRBeeApi/Services/IQRBeeAPI.cs @@ -16,5 +16,12 @@ namespace QRBee.Api.Services /// Registration response Task Register(RegistrationRequest value); + /// + /// Handles Update request + /// + /// + /// Update request + Task Update(string clientId, RegistrationRequest value); + } } diff --git a/QRBeeApi/Services/QRBeeAPI.cs b/QRBeeApi/Services/QRBeeAPI.cs index 492f896..f39bf31 100644 --- a/QRBeeApi/Services/QRBeeAPI.cs +++ b/QRBeeApi/Services/QRBeeAPI.cs @@ -26,13 +26,19 @@ namespace QRBee.Api.Services Validate(request); var info = Convert(request); - //TODO Check that user doesn't exist(name,surname,dateOfBirth or Telephone number(get from Android)) var clientId = await _storage.PutUserInfo(info); return new RegistrationResponse{ClientId = clientId}; } - //TODO Write an UpdateUser command + + public Task Update(string clientId, RegistrationRequest request) + { + Validate(request); + var info = Convert(request); + return _storage.UpdateUser(info); + } + private static void Validate(RegistrationRequest request) { if (request == null) @@ -66,7 +72,7 @@ namespace QRBee.Api.Services } - private UserInfo Convert(RegistrationRequest request) + private static UserInfo Convert(RegistrationRequest request) { return new UserInfo(request.Name, request.Email, request.DateOfBirth); }