using MonkeyCache.FileStore; using System; using System.Threading.Tasks; using TINK.Model.Connector; using TINK.Model.Repository.Request; using TINK.Model.Repository.Response; using TINK.Model.Services.CopriApi; using TINK.Repository.Response; namespace TINK.Model.Repository { public class CopriCallsMonkeyStore : ICopriCache { /// Prevents concurrent communictation. private object monkeyLock = new object(); /// Builds requests. private IRequestBuilder requestBuilder; public const string BIKESAVAILABLE = @"{ ""copri_version"" : ""3.0.0.0"", ""bikes"" : {}, ""response_state"" : ""OK"", ""apiserver"" : ""https://app.tink-konstanz.de"", ""authcookie"" : """", ""response"" : ""bikes_available"" }"; public const string BIKESOCCUPIED = @"{ ""debuglevel"" : ""1"", ""user_id"" : """", ""response"" : ""user_bikes_occupied"", ""user_group"" : ""Konrad,TINK"", ""authcookie"" : """", ""response_state"" : ""OK"", ""bikes_occupied"" : {}, ""copri_version"" : ""3.0.0.0"", ""apiserver"" : ""https://app.tink-konstanz.de"" }"; public const string STATIONS = @"{ ""apiserver"" : ""https://app.tink-konstanz.de"", ""authcookie"" : """", ""response"" : ""stations_all"", ""copri_version"" : ""3.0.0.0"", ""stations"" : {}, ""response_state"" : ""OK"" }"; /// /// Holds the seconds after which station and bikes info is considered to be invalid. /// Default value 1s. /// private TimeSpan ExpiresAfter { get; } /// Returns false because cached values are returned. public bool IsConnected => false; /// Gets the merchant id. public string MerchantId => requestBuilder.MerchantId; /// Gets the merchant id. public string SessionCookie => requestBuilder.SessionCookie; /// Initializes a instance of the copri monkey store object. /// Id of the merchant. /// Session cookie if user is logged in, null otherwise. public CopriCallsMonkeyStore( string merchantId, string sessionCookie = null, TimeSpan? expiresAfter = null) { ExpiresAfter = expiresAfter ?? TimeSpan.FromSeconds(1); requestBuilder = string.IsNullOrEmpty(sessionCookie) ? new RequestBuilder(merchantId) as IRequestBuilder : new RequestBuilderLoggedIn(merchantId, sessionCookie); // Ensure that store holds valid entries. if (!Barrel.Current.Exists(requestBuilder.GetBikesAvailable())) { AddToCache(JsonConvert.DeserializeObject(BIKESAVAILABLE), new TimeSpan(0)); } // Do not query bikes occupied if no user is logged in (leads to not implemented exception) if (!string.IsNullOrEmpty(sessionCookie) && !Barrel.Current.Exists(requestBuilder.GetBikesOccupied())) { AddToCache(JsonConvert.DeserializeObject(BIKESOCCUPIED), new TimeSpan(0)); } if (!Barrel.Current.Exists(requestBuilder.GetStations())) { AddToCache(JsonConvert.DeserializeObject(STATIONS), new TimeSpan(0)); } } public Task DoReserveAsync(int bikeId, Uri operatorUri) { throw new System.Exception("Reservierung im Offlinemodus nicht möglich!"); } public Task DoCancelReservationAsync(int p_iBikeId, Uri operatorUri) { throw new System.Exception("Abbrechen einer Reservierung im Offlinemodus nicht möglich!"); } public Task CalculateAuthKeysAsync(int bikeId, Uri operatorUri) => throw new System.Exception("Schlosssuche im Offlinemodus nicht möglich!"); public Task UpdateLockingStateAsync( int bikeId, LocationDto geolocation, lock_state state, double batteryLevel, Uri operatorUri) => throw new System.Exception("Aktualisierung des Schlossstatuses im Offlinemodus nicht möglich!"); public Task DoBookAsync(int bikeId, Guid guid, double batteryPercentage, Uri operatorUri) { throw new System.Exception("Buchung im Offlinemodus nicht möglich!"); } public Task DoReturn(int bikeId, LocationDto geolocation, Uri operatorUri) { throw new System.Exception("Rückgabe im Offlinemodus nicht möglich!"); } public Task DoSubmitFeedback(string message, bool isBikeBroken, Uri operatorUri) => throw new System.Exception("Übermittlung von Feedback im Offlinemodus nicht möglich!"); public Task DoAuthorizationAsync(string p_strMailAddress, string p_strPassword, string p_strDeviceId) { throw new System.Exception("Anmelden im Offlinemodus nicht möglich!"); } public Task DoAuthoutAsync() { throw new System.Exception("Abmelden im Offlinemodus nicht möglich!"); } public async Task GetBikesAvailableAsync() { var l_oBikesAvailableTask = new TaskCompletionSource(); lock (monkeyLock) { l_oBikesAvailableTask.SetResult(Barrel.Current.Get(requestBuilder.GetBikesAvailable())); } return await l_oBikesAvailableTask.Task; } public async Task GetBikesOccupiedAsync() { try { var l_oBikesOccupiedTask = new TaskCompletionSource(); lock (monkeyLock) { l_oBikesOccupiedTask.SetResult(Barrel.Current.Get(requestBuilder.GetBikesOccupied())); } return await l_oBikesOccupiedTask.Task; } catch (NotSupportedException) { // No user logged in. await Task.CompletedTask; return ResponseHelper.GetBikesOccupiedNone(); } } public async Task GetStationsAsync() { var l_oStationsAllTask = new TaskCompletionSource(); lock (monkeyLock) { l_oStationsAllTask.SetResult(Barrel.Current.Get(requestBuilder.GetStations())); } return await l_oStationsAllTask.Task; } /// Gets a value indicating whether stations are expired or not. public bool IsStationsExpired { get { lock (monkeyLock) { return Barrel.Current.IsExpired(requestBuilder.GetStations()); } } } /// Adds a stations all response to cache. /// Stations to add. public void AddToCache(StationsAllResponse stations) { AddToCache(stations, ExpiresAfter); } /// Adds a stations all response to cache. /// Stations to add. /// Time after which anser is considered to be expired. private void AddToCache(StationsAllResponse stations, TimeSpan expiresAfter) { lock (monkeyLock) { Barrel.Current.Add( requestBuilder.GetStations(), JsonConvert.SerializeObject(stations), expiresAfter); } } /// Gets a value indicating whether stations are expired or not. public bool IsBikesAvailableExpired { get { lock (monkeyLock) { return Barrel.Current.IsExpired(requestBuilder.GetBikesAvailable()); } } } /// Adds a bikes response to cache. /// Bikes to add. public void AddToCache(BikesAvailableResponse bikes) { AddToCache(bikes, ExpiresAfter); } /// Adds a bikes response to cache. /// Bikes to add. /// Time after which anser is considered to be expired. private void AddToCache(BikesAvailableResponse bikes, TimeSpan expiresAfter) { lock (monkeyLock) { Barrel.Current.Add( requestBuilder.GetBikesAvailable(), JsonConvert.SerializeObject(bikes), expiresAfter); } } /// Gets a value indicating whether stations are expired or not. public bool IsBikesOccupiedExpired { get { lock (monkeyLock) { return Barrel.Current.IsExpired(requestBuilder.GetBikesOccupied()); } } } /// Adds a bikes response to cache. /// Bikes to add. public void AddToCache(BikesReservedOccupiedResponse bikes) { AddToCache(bikes, ExpiresAfter); } /// Adds a bikes response to cache. /// Bikes to add. /// Time after which anser is considered to be expired. private void AddToCache(BikesReservedOccupiedResponse bikes, TimeSpan expiresAfter) { lock (monkeyLock) { Barrel.Current.Add( requestBuilder.GetBikesOccupied(), JsonConvert.SerializeObject(bikes), expiresAfter); } } } }